Код: Выделить всё
#!/bin/bash
# ============================================================
# SSH MONITOR С АВТОБАНОМ
# Сервер: daemon
# Установлен: 2026-09-10
# ============================================================
# ==================== НАСТРОЙКИ ====================
EMAIL_TO="admin@padonak.su"
SERVER_NAME="daemon"
ALLOWED_IPS=(
"93.95.101.238"
"192.168.13.0/24"
)
BAN_TIME="86400" # 24 часа
AUTO_BLOCK="yes"
NOTIFY_SUSPICIOUS="yes"
# ==================== СЛУЖЕБНЫЕ ПЕРЕМЕННЫЕ ====================
EMAIL_FROM="ssh-alert@$SERVER_NAME"
LOG_FILE="/var/log/ssh-monitor.log"
BAN_LOG="/var/log/ssh-ban.log"
STATE_DIR="/var/lib/ssh-monitor"
STATE_FILE="$STATE_DIR/last_state"
PID_FILE="/var/run/ssh-monitor.pid"
# ==================== ФУНКЦИИ ====================
log_message() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}
log_ban() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$BAN_LOG"
}
send_email() {
local subject="$1"
local body="$2"
local email_content="From: $EMAIL_FROM
To: $EMAIL_TO
Subject: $subject
Content-Type: text/plain; charset=UTF-8
$body
---
Server: $SERVER_NAME
IP: $(hostname -I | awk '{print $1}')
Time: $(date '+%Y-%m-%d %H:%M:%S')
"
log_message "📧 Отправка письма на $EMAIL_TO"
# Способ 1: msmtp
if command -v msmtp >/dev/null 2>&1; then
if echo "$email_content" | msmtp -t "$EMAIL_TO" 2>/dev/null; then
log_message " ✅ Отправлено через msmtp"
return 0
else
log_message " ❌ Ошибка msmtp"
fi
fi
# Способ 2: sendmail
if command -v sendmail >/dev/null 2>&1; then
if echo "$email_content" | sendmail -t 2>/dev/null; then
log_message " ✅ Отправлено через sendmail"
return 0
fi
fi
# Способ 3: mail
if command -v mail >/dev/null 2>&1; then
if echo "$body" | mail -s "$subject" -a "From: $EMAIL_FROM" "$EMAIL_TO" 2>/dev/null; then
log_message " ✅ Отправлено через mail"
return 0
fi
fi
log_message "❌ ВСЕ СПОСОБЫ НЕ СРАБОТАЛИ!"
return 1
}
# ==================== ФУНКЦИЯ ПРОВЕРКИ ПОДСЕТЕЙ ====================
is_ip_allowed() {
local ip="$1"
if [[ -z "$ip" ]] || [[ "$ip" == "unknown" ]]; then
return 1
fi
for allowed in "${ALLOWED_IPS[@]}"; do
# Точное совпадение
if [[ "$ip" == "$allowed" ]]; then
return 0
fi
# Проверка подсети (поддерживает /8, /16, /22, /24, /32 и т.д.)
if [[ "$allowed" == *"/"* ]]; then
local network=$(echo "$allowed" | cut -d'/' -f1)
local mask=$(echo "$allowed" | cut -d'/' -f2)
# Конвертируем IP в число
ip_to_int() {
local a b c d
IFS=. read -r a b c d <<< "$1"
echo "$((a * 256**3 + b * 256**2 + c * 256 + d))"
}
local ip_int=$(ip_to_int "$ip")
local net_int=$(ip_to_int "$network")
local mask_int=$((0xffffffff << (32 - mask) & 0xffffffff))
if [[ $((ip_int & mask_int)) -eq $((net_int & mask_int)) ]]; then
return 0
fi
fi
done
return 1
}
get_ip_for_pid() {
local pid="$1"
local ip=""
if command -v ss >/dev/null 2>&1; then
ip=$(ss -tnp 2>/dev/null | grep "pid=$pid" | head -1 | awk '{print $5}' | cut -d':' -f1)
if [[ -n "$ip" ]] && [[ "$ip" != "0.0.0.0" ]] && [[ "$ip" != "::" ]] && [[ "$ip" != "*" ]]; then
echo "$ip"
return 0
fi
fi
if command -v netstat >/dev/null 2>&1; then
ip=$(netstat -tnp 2>/dev/null | grep "$pid/sshd" | head -1 | awk '{print $5}' | cut -d':' -f1)
if [[ -n "$ip" ]] && [[ "$ip" != "0.0.0.0" ]] && [[ "$ip" != "::" ]] && [[ "$ip" != "*" ]]; then
echo "$ip"
return 0
fi
fi
local tty=$(ps -o tty= -p "$pid" 2>/dev/null | tr -d ' ')
if [[ -n "$tty" ]] && [[ "$tty" != "?" ]]; then
ip=$(who -u 2>/dev/null | grep "$tty" | awk '{print $NF}' | sed 's/[()]//g')
if [[ -n "$ip" ]] && [[ "$ip" != "0.0.0.0" ]] && [[ "$ip" != "::" ]] && [[ "$ip" != "*" ]]; then
echo "$ip"
return 0
fi
fi
echo "unknown"
}
get_ssh_sessions() {
ps aux | grep -E 'sshd-session:.*@|sshd:.*@' | grep -v grep | grep -v "listener" | grep -v "accepted" | awk '{print $2}'
}
block_ip() {
local ip="$1"
log_ban "🔒 БЛОКИРОВКА IP: $ip"
if iptables -L INPUT -n 2>/dev/null | grep -q "$ip"; then
log_ban "ℹ️ IP $ip уже заблокирован"
return 0
fi
if command -v iptables >/dev/null 2>&1; then
if [[ "$BAN_TIME" -gt 0 ]]; then
iptables -I INPUT -s "$ip" -m recent --set --name SSH_BAN 2>/dev/null
iptables -I INPUT -s "$ip" -m recent --update --seconds $BAN_TIME --name SSH_BAN -j DROP 2>/dev/null
log_ban "✅ IP $ip заблокирован на $BAN_TIME секунд ($(($BAN_TIME / 3600)) часов)"
else
iptables -I INPUT -s "$ip" -j DROP 2>/dev/null
log_ban "✅ IP $ip заблокирован НАВСЕГДА"
fi
return 0
fi
if command -v ufw >/dev/null 2>&1; then
ufw deny from "$ip" 2>/dev/null
log_ban "✅ IP $ip заблокирован через ufw"
return 0
fi
log_ban "❌ НЕ УДАЛОСЬ ЗАБЛОКИРОВАТЬ IP $ip"
return 1
}
process_session() {
local pid="$1"
local user=$(ps -o user= -p "$pid" 2>/dev/null | tr -d ' ')
local client_ip=$(get_ip_for_pid "$pid")
local start_time=$(ps -o lstart= -p "$pid" 2>/dev/null | tr -d ' ')
log_message "NEW SESSION: PID=$pid, USER=$user, IP=$client_ip"
if [[ -z "$client_ip" ]] || [[ "$client_ip" == "unknown" ]]; then
log_message "FAILED to get IP for PID $pid"
return 1
fi
# Проверка IP с поддержкой подсетей
if is_ip_allowed "$client_ip"; then
log_message "✅ ALLOWED IP: $client_ip"
return 0
fi
log_message "🚨 SUSPICIOUS IP: $client_ip"
if [[ "$AUTO_BLOCK" == "yes" ]]; then
block_ip "$client_ip"
fi
if [[ "$NOTIFY_SUSPICIOUS" == "yes" ]]; then
local ban_text=""
if [[ "$BAN_TIME" -gt 0 ]]; then
ban_text="на $(($BAN_TIME / 3600)) часов"
else
ban_text="НАВСЕГДА"
fi
local subject="🚨 SSH БАН: IP $client_ip заблокирован!"
local body="🚨 ПОДОЗРИТЕЛЬНОЕ SSH ПОДКЛЮЧЕНИЕ
========================================
СТАТУС: ❌ НЕ РАЗРЕШЕН!
Пользователь: $user
IP адрес: $client_ip
Время подключения: $start_time
Сервер: $SERVER_NAME
========================================
🔴 IP АДРЕС ЗАБЛОКИРОВАН $ban_text!
Причина: Подключение с неразрешенного IP
Действие: Автоматическая блокировка через iptables
Текущие активные сессии:
$(who -u 2>/dev/null || echo "Нет активных сессий")
Заблокированные IP:
$(iptables -L INPUT -n 2>/dev/null | grep -E "DROP|REJECT" | head -10 || echo "Нет блокировок")
========================================"
send_email "$subject" "$body"
fi
}
show_status() {
echo "========================================="
echo "SSH MONITOR WITH AUTO-BAN - STATUS"
echo "========================================="
echo ""
echo "Server: $SERVER_NAME"
echo "Email: $EMAIL_TO"
echo "Allowed IPs:"
for ip in "${ALLOWED_IPS[@]}"; do
echo " - $ip"
done
echo ""
echo "Ban time: $BAN_TIME seconds ($(($BAN_TIME / 3600)) hours)"
echo "Auto block: $AUTO_BLOCK"
echo ""
echo "Blocked IPs:"
iptables -L INPUT -n 2>/dev/null | grep -E "DROP|REJECT" | head -20 || echo " No blocked IPs"
echo ""
echo "Log: $LOG_FILE"
echo "Ban log: $BAN_LOG"
echo "========================================="
}
test_email() {
echo "Sending test email to $EMAIL_TO..."
local subject="🧪 Test from SSH Monitor with Auto-Ban"
local body="TEST EMAIL
========================================
This is a test email from SSH Monitor.
System is working with AUTO-BAN enabled.
Server: $SERVER_NAME
IP: $(hostname -I | awk '{print $1}')
Time: $(date '+%Y-%m-%d %H:%M:%S')
========================================"
if send_email "$subject" "$body"; then
echo "✅ Test email sent!"
else
echo "❌ Failed to send test email"
fi
}
case "$1" in
--status|status)
show_status
exit 0
;;
--test|test)
test_email
exit 0
;;
--unban|unban)
if [[ -z "$2" ]]; then
echo "Usage: $0 --unban IP"
exit 1
fi
iptables -D INPUT -s "$2" -j DROP 2>/dev/null
iptables -D INPUT -s "$2" -m recent --update --name SSH_BAN -j DROP 2>/dev/null
echo "✅ IP $2 разблокирован"
exit 0
;;
--help|help|-h)
echo "Usage: $0 [OPTION]"
echo " --status - Show status"
echo " --test - Send test email"
echo " --unban IP - Unblock IP"
echo " --help - Show this help"
exit 0
;;
esac
mkdir -p "$(dirname "$LOG_FILE")" "$STATE_DIR"
touch "$LOG_FILE" "$BAN_LOG"
if [[ -f "$PID_FILE" ]]; then
OLD_PID=$(cat "$PID_FILE" 2>/dev/null)
if kill -0 "$OLD_PID" 2>/dev/null; then
echo "❌ Script already running (PID: $OLD_PID)"
exit 1
fi
fi
echo $$ > "$PID_FILE"
log_message "========================================="
log_message "🚀 START SSH MONITOR WITH AUTO-BAN"
log_message "Server: $SERVER_NAME"
log_message "Email: $EMAIL_TO"
log_message "Allowed IPs: ${ALLOWED_IPS[*]}"
log_message "Ban time: $BAN_TIME seconds"
log_message "Auto block: $AUTO_BLOCK"
log_message "========================================="
EXISTING_SESSIONS=$(get_ssh_sessions)
if [[ -n "$EXISTING_SESSIONS" ]]; then
log_message "Found existing sessions: $(echo "$EXISTING_SESSIONS" | wc -w)"
fi
echo "$EXISTING_SESSIONS" > "$STATE_FILE"
log_message "Initialization complete"
while true; do
CURRENT_SESSIONS=$(get_ssh_sessions)
if [[ -n "$CURRENT_SESSIONS" ]]; then
PREVIOUS_SESSIONS=$(cat "$STATE_FILE" 2>/dev/null || echo "")
for pid in $CURRENT_SESSIONS; do
if ! echo "$PREVIOUS_SESSIONS" | grep -q "^$pid$"; then
process_session "$pid"
fi
done
echo "$CURRENT_SESSIONS" > "$STATE_FILE"
else
echo "" > "$STATE_FILE" 2>/dev/null
fi
sleep 5
done
Код: Выделить всё
vim install-ssh-monitor.sh# 3. Сделайте исполняемым
Код: Выделить всё
chmod +x install-ssh-monitor.shКод: Выделить всё
./install-ssh-monitor.shЗапросит email для уведомлений
Запросит разрешенные IP (можно добавить несколько)
Запросит интервал проверки (по умолчанию 5 секунд)
Спросит про автоблокировку (Да/Нет)
Спросит про уведомления (только подозрительные или все)
Покажет все настройки и запросит подтверждение
Остановит старый сервис (если был)
Установит новый скрипт и сервис
Отправит тестовое письмо (по желанию)
Покажет команды для управления
