Страница 1 из 1

SSH MONITOR - СКРИПТ ДЛЯ ОТСЛЕЖИВАНИЯ ПОДОЗРИТЕЛЬНЫХ ПОДКЛЮЧЕНИЙ

Добавлено: 05 сен 2026, 16:24
Padonak

Код: Выделить всё

#!/bin/bash
# ============================================================
# SSH MONITOR С АВТОБАНОМ (IPSet + Shorewall)
# ИНТЕРАКТИВНАЯ УСТАНОВКА - ВЕРСИЯ 15.0 (ФИНАЛЬНАЯ)
# ============================================================
# ПРОВЕРЕНО НА:
#   - Ubuntu 26.04 LTS
#   - Shorewall 5.2.8
#   - IPSet v7.19
#   - msmtp
# ============================================================

if [[ $EUID -ne 0 ]]; then
   echo "❌ Ошибка: Запустите от root (sudo)"
   exit 1
fi

# ==================== ЦВЕТА ====================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

print_header() {
    echo ""
    echo -e "${BLUE}========================================${NC}"
    echo -e "${CYAN}  $1${NC}"
    echo -e "${BLUE}========================================${NC}"
    echo ""
}

print_success() { echo -e "${GREEN}✅ $1${NC}"; }
print_error() { echo -e "${RED}❌ $1${NC}"; }
print_info() { echo -e "${YELLOW}ℹ️  $1${NC}"; }
print_question() { echo -e "${CYAN}❓ $1${NC}"; }

# ==================== ФУНКЦИЯ ВВОДА ====================
get_input() {
    local prompt="$1"
    local default="$2"
    local input=""
    
    if [[ -n "$default" ]]; then
        read -p "$prompt [$default]: " input
        input="${input:-$default}"
    else
        read -p "$prompt: " input
    fi
    
    echo "$input"
}

# ==================== ПРОВЕРКА EMAIL ====================
validate_email() {
    local email="$1"
    [[ "$email" =~ ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$ ]]
}

# ==================== ПРОВЕРКА IP ====================
validate_ip() {
    local ip="$1"
    if [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        return 0
    elif [[ "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
        return 0
    else
        return 1
    fi
}

# ==================== ПРОВЕРКА ЗАВИСИМОСТЕЙ ====================
check_dependencies() {
    print_header "📦 ПРОВЕРКА ЗАВИСИМОСТЕЙ"
    
    local deps_ok=true
    
    # ipset
    if command -v ipset >/dev/null 2>&1; then
        print_success "ipset установлен"
    else
        print_info "Устанавливаю ipset..."
        apt update -qq && apt install -y ipset >/dev/null 2>&1
        if command -v ipset >/dev/null 2>&1; then
            print_success "ipset установлен"
        else
            print_error "Не удалось установить ipset"
            deps_ok=false
        fi
    fi
    
    # iptables
    if command -v iptables >/dev/null 2>&1; then
        print_success "iptables установлен"
    else
        print_error "iptables не найден"
        deps_ok=false
    fi
    
    # Shorewall
    if command -v shorewall >/dev/null 2>&1; then
        print_success "Shorewall установлен ($(shorewall version))"
    else
        print_error "Shorewall не найден!"
        print_info "Установите: apt install shorewall"
        deps_ok=false
    fi
    
    # netfilter-persistent (опционально)
    if command -v netfilter-persistent >/dev/null 2>&1; then
        print_success "netfilter-persistent установлен"
    else
        print_info "Устанавливаю iptables-persistent..."
        DEBIAN_FRONTEND=noninteractive apt install -y iptables-persistent >/dev/null 2>&1
        if command -v netfilter-persistent >/dev/null 2>&1; then
            print_success "netfilter-persistent установлен"
        fi
    fi
    
    # Загружаем модули ядра
    modprobe xt_set 2>/dev/null
    modprobe ip_set 2>/dev/null
    modprobe ip_set_hash_ip 2>/dev/null
    
    if lsmod | grep -q "xt_set"; then
        print_success "Модуль xt_set загружен"
    else
        print_error "Модуль xt_set не загружен"
        deps_ok=false
    fi
    
    # Автозагрузка модулей
    if [[ ! -f /etc/modules-load.d/ipset.conf ]]; then
        cat > /etc/modules-load.d/ipset.conf << 'EOF'
xt_set
ip_set
ip_set_hash_ip
EOF
        print_success "Модули добавлены в автозагрузку"
    fi
    
    if [[ "$deps_ok" == false ]]; then
        print_error "Не все зависимости установлены"
        exit 1
    fi
    
    echo ""
}

# ==================== НАСТРОЙКА ПОЧТЫ ====================
setup_mail() {
    print_header "📧 НАСТРОЙКА ПОЧТЫ"
    
    echo -e "${YELLOW}Хотите настроить отправку email?${NC}"
    echo "  1) Да, настроить msmtp (рекомендуется)"
    echo "  2) Нет, использовать существующие sendmail/mail"
    echo "  3) Нет, только логи (без email)"
    read -p "Выберите [1-3]: " mail_choice
    
    case "$mail_choice" in
        1)
            if ! command -v msmtp >/dev/null 2>&1; then
                print_info "Устанавливаю msmtp..."
                apt update -qq && apt install -y msmtp msmtp-mta ca-certificates >/dev/null 2>&1
            fi
            
            if ! command -v msmtp >/dev/null 2>&1; then
                print_error "Не удалось установить msmtp"
                SMTP_FROM="ssh-alert@$(hostname -f)"
                return 1
            fi
            
            print_success "msmtp установлен"
            echo ""
            
            SMTP_HOST=$(get_input "🌐 SMTP сервер" "smtp.padonak.su")
            SMTP_PORT=$(get_input "🔌 SMTP порт" "587")
            
            echo ""
            echo -e "${YELLOW}Тип шифрования:${NC}"
            echo "  1) STARTTLS (порт 587) - рекомендуется"
            echo "  2) SSL/TLS (порт 465)"
            echo "  3) Без шифрования (порт 25)"
            read -p "Выберите [1-3]: " tls_choice
            
            case "$tls_choice" in
                1) SMTP_TLS="on"; SMTP_STARTTLS="on"; SMTP_TLS_TEXT="STARTTLS" ;;
                2) SMTP_TLS="on"; SMTP_STARTTLS="off"; SMTP_TLS_TEXT="SSL/TLS" ;;
                *) SMTP_TLS="off"; SMTP_STARTTLS="off"; SMTP_TLS_TEXT="Без шифрования" ;;
            esac
            print_success "Шифрование: $SMTP_TLS_TEXT"
            echo ""
            
            SMTP_USER=$(get_input "👤 SMTP логин" "alert@padonak.su")
            
            read -sp "🔑 SMTP пароль: " SMTP_PASS
            echo ""
            
            if [[ -z "$SMTP_PASS" ]]; then
                print_error "Пароль не может быть пустым!"
                SMTP_FROM="ssh-alert@$(hostname -f)"
                return 1
            fi
            
            SMTP_FROM=$(get_input "📧 Email отправителя" "$SMTP_USER")
            
            cat > /etc/msmtprc << EOF
# ============================================================
# MSMTP Configuration
# Создан: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================================

defaults
auth           on
tls            $SMTP_TLS
tls_starttls   $SMTP_STARTTLS
tls_trust_file /etc/ssl/certs/ca-certificates.crt
logfile        /var/log/msmtp.log

account        default
host           $SMTP_HOST
port           $SMTP_PORT
from           $SMTP_FROM
user           $SMTP_USER
password       $SMTP_PASS
EOF

            chmod 600 /etc/msmtprc
            chown root:root /etc/msmtprc
            print_success "Конфиг создан: /etc/msmtprc"
            ;;
        2)
            print_info "Используем существующие sendmail/mail"
            SMTP_FROM="ssh-alert@$(hostname -f)"
            SMTP_HOST=""
            SMTP_PORT=""
            SMTP_USER=""
            SMTP_PASS=""
            ;;
        *)
            print_info "Почта не настроена, только логи"
            SMTP_FROM="ssh-alert@$(hostname -f)"
            SMTP_HOST=""
            SMTP_PORT=""
            SMTP_USER=""
            SMTP_PASS=""
            ;;
    esac
    
    echo ""
    return 0
}

# ==================== НАСТРОЙКА SHOREWALL ====================
setup_shorewall() {
    print_header "🔥 НАСТРОЙКА SHOREWALL"
    
    local IPSET_NAME="ssh_ban"
    
    # 1. Создаём initdone (Perl синтаксис!)
    print_info "Создаю /etc/shorewall/initdone..."
    
    cat > /etc/shorewall/initdone << 'EOF'
# ============================================================
# Shorewall initdone - выполняется при старте Shorewall
# Использует Perl синтаксис!
# ============================================================

# Загружаем модули ядра
system("modprobe xt_set");
system("modprobe ip_set");
system("modprobe ip_set_hash_ip");

# Создаём ipset для блокировки SSH (не удаляем если существует)
system("ipset create ssh_ban hash:ip timeout 86400 -exist");

1;
EOF
    
    chmod 755 /etc/shorewall/initdone
    print_success "Создан /etc/shorewall/initdone"
    
    # 2. Добавляем правило в rules (если ещё нет)
    if ! grep -q "net:+ssh_ban" /etc/shorewall/rules 2>/dev/null; then
        print_info "Добавляю правило в /etc/shorewall/rules..."
        
        # Бэкап
        cp /etc/shorewall/rules "/etc/shorewall/rules.backup.$(date +%Y%m%d_%H%M%S)"
        
        # Добавляем правило в правильном формате (net:+ssh_ban с плюсом!)
        cat >> /etc/shorewall/rules << 'EOF'

# ============================================================
# SSH BAN через ipset - блокировка неразрешённых IP
# ВАЖНО: должно быть ПОСЛЕ всех ACCEPT для SSH!
# ============================================================
DROP            net:+ssh_ban     all
EOF
        
        print_success "Правило добавлено в /etc/shorewall/rules"
    else
        print_success "Правило уже есть в /etc/shorewall/rules"
    fi
    
    # 3. Проверяем конфиг
    print_info "Проверка конфига Shorewall..."
    if shorewall check >/dev/null 2>&1; then
        print_success "Конфиг Shorewall корректен"
    else
        print_error "Ошибка в конфиге Shorewall!"
        shorewall check
        exit 1
    fi
    
    # 4. Перезапускаем Shorewall
    print_info "Перезапуск Shorewall..."
    if shorewall restart >/dev/null 2>&1; then
        print_success "Shorewall перезапущен"
    else
        print_error "Не удалось перезапустить Shorewall"
        shorewall restart
        exit 1
    fi
    
    # 5. Проверяем ipset
    if ipset list "$IPSET_NAME" >/dev/null 2>&1; then
        print_success "ipset $IPSET_NAME создан"
    else
        print_error "ipset $IPSET_NAME НЕ создан!"
        print_info "Создаю вручную..."
        modprobe xt_set 2>/dev/null
        modprobe ip_set 2>/dev/null
        modprobe ip_set_hash_ip 2>/dev/null
        ipset create "$IPSET_NAME" hash:ip timeout 86400 -exist
    fi
    
    # 6. Проверяем правило
    if iptables -n -L net-fw 2>/dev/null | grep -q "ssh_ban"; then
        print_success "Правило iptables для ssh_ban активно"
    else
        print_error "Правило iptables для ssh_ban НЕ найдено!"
    fi
    
    echo ""
}

# ==================== ЗАГОЛОВОК ====================
clear
print_header "🔐 SSH MONITOR - ИНТЕРАКТИВНАЯ УСТАНОВКА v15.0 (SHOREWALL)"

echo -e "${YELLOW}Этот скрипт установит систему мониторинга SSH подключений.${NC}"
echo -e "${YELLOW}Работает совместно с Shorewall + ipset!${NC}"
echo -e "${YELLOW}Подозрительные IP блокируются и АВТОМАТИЧЕСКИ разблокируются!${NC}"
echo ""

# ==================== 0. ЗАВИСИМОСТИ ====================
check_dependencies

# ==================== 1. ПОЧТА ====================
setup_mail

# ==================== 2. ИМЯ СЕРВЕРА ====================
print_header "🏷️  ИМЯ СЕРВЕРА"

CURRENT_HOSTNAME=$(cat /etc/hostname 2>/dev/null || echo "unknown")
CURRENT_FQDN=$(hostname -f 2>/dev/null || echo "unknown")

echo -e "${CYAN}Текущее имя:${NC}"
echo -e "  /etc/hostname: ${GREEN}$CURRENT_HOSTNAME${NC}"
echo -e "  hostname -f:   ${YELLOW}$CURRENT_FQDN${NC}"
echo ""

DEFAULT_NAME="$CURRENT_HOSTNAME"
if [[ "$CURRENT_FQDN" != "localhost" ]] && [[ "$CURRENT_FQDN" != "localhost.localdomain" ]]; then
    DEFAULT_NAME="$CURRENT_FQDN"
fi

SERVER_NAME=$(get_input "🌐 Имя сервера для уведомлений" "$DEFAULT_NAME")
print_success "Имя сервера: $SERVER_NAME"
echo ""

# ==================== 3. EMAIL ====================
print_header "📧 EMAIL ДЛЯ УВЕДОМЛЕНИЙ"

echo -e "${YELLOW}Можно указать несколько email через запятую или пробел.${NC}"
echo -e "${YELLOW}Пример: admin@domain.ru, security@domain.ru${NC}"
echo ""

while true; do
    EMAIL_INPUT=$(get_input "📧 Email для уведомлений" "admin@padonak.su")
    
    EMAIL_INPUT_CLEAN=$(echo "$EMAIL_INPUT" | tr ',' ' ')
    EMAILS_TO=()
    ALL_VALID=true
    
    for email in $EMAIL_INPUT_CLEAN; do
        if validate_email "$email"; then
            EMAILS_TO+=("$email")
        else
            print_error "Неверный формат email: $email"
            ALL_VALID=false
        fi
    done
    
    if [[ "$ALL_VALID" == true ]] && [[ ${#EMAILS_TO[@]} -gt 0 ]]; then
        print_success "Принято email: ${#EMAILS_TO[@]}"
        for email in "${EMAILS_TO[@]}"; do
            echo -e "  ${GREEN}•${NC} $email"
        done
        break
    else
        print_error "Попробуйте снова."
    fi
done
echo ""

# ==================== 4. РАЗРЕШЕННЫЕ IP ====================
print_header "🛡️ РАЗРЕШЕННЫЕ IP АДРЕСА"

echo -e "${YELLOW}Укажите IP адреса одной строкой через пробел или запятую.${NC}"
echo -e "${YELLOW}Можно указывать отдельные IP и подсети.${NC}"
echo ""
echo -e "${YELLOW}📌 Пример:${NC}"
echo -e "  ${GREEN}93.95.101.238 79.164.47.234 185.215.60.0/22${NC}"
echo ""

CURRENT_IP=$(hostname -I | awk '{print $1}')
if [[ -z "$CURRENT_IP" ]]; then
    CURRENT_IP="127.0.0.1"
fi

echo -e "${YELLOW}💡 Текущий IP сервера: $CURRENT_IP${NC}"
echo ""

while true; do
    read -p "Введите IP адреса: " IP_INPUT
    
    if [[ -z "$IP_INPUT" ]]; then
        print_info "Добавляю текущий IP: $CURRENT_IP"
        ALLOWED_IPS=("$CURRENT_IP")
        break
    fi
    
    IP_INPUT_CLEAN=$(echo "$IP_INPUT" | tr ',;\t' ' ')
    ALLOWED_IPS=()
    ALL_VALID=true
    
    for ip in $IP_INPUT_CLEAN; do
        if validate_ip "$ip"; then
            ALLOWED_IPS+=("$ip")
        else
            print_error "Неверный формат IP: $ip"
            ALL_VALID=false
        fi
    done
    
    if [[ "$ALL_VALID" == true ]] && [[ ${#ALLOWED_IPS[@]} -gt 0 ]]; then
        break
    else
        print_error "Попробуйте снова."
        echo ""
    fi
done

echo ""
print_success "📋 Добавлено разрешенных IP: ${#ALLOWED_IPS[@]}"
for ip in "${ALLOWED_IPS[@]}"; do
    echo -e "  ${GREEN}•${NC} $ip"
done
echo ""

# ==================== 5. НАСТРОЙКИ БАНА ====================
print_header "🔒 НАСТРОЙКИ АВТОБЛОКИРОВКИ"

echo -e "${CYAN}⏱️  Время блокировки (автоматический разбан):${NC}"
echo "  1) 1 час"
echo "  2) 24 часа (рекомендуется)"
echo "  3) 7 дней"
echo "  4) 30 дней"
read -p "Выберите [1-4]: " ban_time_choice

case "$ban_time_choice" in
    1) BAN_TIME="3600"; BAN_TEXT="1 час" ;;
    2) BAN_TIME="86400"; BAN_TEXT="24 часа" ;;
    3) BAN_TIME="604800"; BAN_TEXT="7 дней" ;;
    4) BAN_TIME="2592000"; BAN_TEXT="30 дней" ;;
    *) BAN_TIME="86400"; BAN_TEXT="24 часа" ;;
esac
print_success "Время блокировки: $BAN_TEXT"
echo ""

echo -e "${CYAN}⚡ Действие при подозрительном подключении:${NC}"
echo "  1) Блокировать + уведомление (рекомендуется)"
echo "  2) Только блокировка (без уведомлений)"
echo "  3) Только уведомление (без блокировки)"
read -p "Выберите [1-3]: " action_choice

case "$action_choice" in
    2) AUTO_BLOCK="yes"; NOTIFY_SUSPICIOUS="no"; ACTION_TEXT="Только блокировка" ;;
    3) AUTO_BLOCK="no"; NOTIFY_SUSPICIOUS="yes"; ACTION_TEXT="Только уведомление" ;;
    *) AUTO_BLOCK="yes"; NOTIFY_SUSPICIOUS="yes"; ACTION_TEXT="Блокировка + уведомление" ;;
esac
print_success "Действие: $ACTION_TEXT"
echo ""

# ==================== 6. ПОДТВЕРЖДЕНИЕ ====================
print_header "📋 ПРОВЕРЬТЕ НАСТРОЙКИ"

if [[ -n "$SMTP_HOST" ]]; then
    echo -e "${CYAN}📧 SMTP:${NC} $SMTP_HOST:$SMTP_PORT ($SMTP_TLS_TEXT)"
    echo -e "${CYAN}👤 SMTP логин:${NC} $SMTP_USER"
fi
echo -e "${CYAN}🔥 Shorewall:${NC} $(shorewall version)"
echo -e "${CYAN}🏷️  Имя сервера:${NC} $SERVER_NAME"
echo -e "${CYAN}📧 Email (${#EMAILS_TO[@]}):${NC}"
for email in "${EMAILS_TO[@]}"; do
    echo -e "  ${GREEN}•${NC} $email"
done
echo -e "${CYAN}🛡️ Разрешенные IP (${#ALLOWED_IPS[@]}):${NC}"
for ip in "${ALLOWED_IPS[@]}"; do
    echo -e "  ${GREEN}•${NC} $ip"
done
echo -e "${CYAN}🔒 Время блокировки:${NC} $BAN_TEXT"
echo -e "${CYAN}⏰ Авторазбан:${NC} ${GREEN}ДА${NC} (через $BAN_TEXT)"
echo -e "${CYAN}⚡ Действие:${NC} $ACTION_TEXT"
echo ""

read -p "Продолжить установку? (y/n): " -n 1 -r
echo ""
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
    print_error "Установка отменена"
    exit 1
fi

# ==================== 7. НАСТРОЙКА SHOREWALL ====================
setup_shorewall

# ==================== 8. ОСТАНОВКА СТАРОГО СЕРВИСА ====================
print_header "🛑 ОСТАНОВКА СТАРОГО СЕРВИСА"

systemctl stop ssh-monitor 2>/dev/null
systemctl stop ssh-monitor-final 2>/dev/null
systemctl disable ssh-monitor 2>/dev/null
systemctl disable ssh-monitor-final 2>/dev/null
rm -f /etc/systemd/system/ssh-monitor.service
rm -f /etc/systemd/system/ssh-monitor-final.service
rm -f /usr/local/bin/ssh-monitor.sh
rm -f /usr/local/bin/ssh-monitor-final.sh
systemctl daemon-reload
print_success "Старый сервис удален"

# ==================== 9. ОЧИСТКА IPSET ====================
print_header "🧹 ОЧИСТКА IPSET"

IPSET_NAME="ssh_ban"

if ipset list "$IPSET_NAME" >/dev/null 2>&1; then
    print_info "Очистка ipset $IPSET_NAME..."
    ipset flush "$IPSET_NAME" 2>/dev/null
    print_success "ipset очищен"
else
    print_info "Создаю ipset..."
    modprobe xt_set 2>/dev/null
    modprobe ip_set 2>/dev/null
    modprobe ip_set_hash_ip 2>/dev/null
    ipset create "$IPSET_NAME" hash:ip timeout $BAN_TIME 2>/dev/null
fi

if ipset list "$IPSET_NAME" >/dev/null 2>&1; then
    print_success "ipset $IPSET_NAME готов"
else
    print_error "ipset $IPSET_NAME не создан!"
    exit 1
fi
echo ""

# ==================== 10. СОЗДАНИЕ СКРИПТА ====================
print_header "📝 СОЗДАНИЕ СКРИПТА"

ALLOWED_IPS_STR=""
for ip in "${ALLOWED_IPS[@]}"; do
    ALLOWED_IPS_STR+="    \"$ip\"\n"
done

EMAILS_TO_STR_BUILD=""
for email in "${EMAILS_TO[@]}"; do
    EMAILS_TO_STR_BUILD+="    \"$email\"\n"
done

cat > /usr/local/bin/ssh-monitor.sh << SCRIPT_EOF
#!/bin/bash
# ============================================================
# SSH MONITOR С АВТОБАНОМ (IPSet + Shorewall)
# Сервер: $SERVER_NAME
# Установлен: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================================

# ==================== НАСТРОЙКИ ====================
EMAILS_TO=(
$(echo -e "$EMAILS_TO_STR_BUILD")
)
SERVER_NAME="$SERVER_NAME"
ALLOWED_IPS=(
$(echo -e "$ALLOWED_IPS_STR")
)
BAN_TIME="$BAN_TIME"
AUTO_BLOCK="$AUTO_BLOCK"
NOTIFY_SUSPICIOUS="$NOTIFY_SUSPICIOUS"
IPSET_NAME="$IPSET_NAME"

# ==================== СЛУЖЕБНЫЕ ПЕРЕМЕННЫЕ ====================
EMAIL_FROM="${SMTP_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: \${EMAILS_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 "📧 Отправка письма на: \${EMAILS_TO[*]}"
    
    for recipient in "\${EMAILS_TO[@]}"; do
        if command -v msmtp >/dev/null 2>&1; then
            if echo "\$email_content" | msmtp -t "\$recipient" 2>/dev/null; then
                log_message "  ✅ Отправлено на \$recipient через msmtp"
                continue
            fi
        fi
        
        if command -v sendmail >/dev/null 2>&1; then
            if echo "\$email_content" | sendmail -t "\$recipient" 2>/dev/null; then
                log_message "  ✅ Отправлено на \$recipient через sendmail"
                continue
            fi
        fi
        
        if command -v mail >/dev/null 2>&1; then
            if echo "\$body" | mail -s "\$subject" -a "From: \$EMAIL_FROM" "\$recipient" 2>/dev/null; then
                log_message "  ✅ Отправлено на \$recipient через mail"
                continue
            fi
        fi
        
        log_message "  ❌ Не удалось отправить на \$recipient"
    done
}

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
        
        if [[ "\$allowed" == *"/"* ]]; then
            local network=\$(echo "\$allowed" | cut -d'/' -f1)
            local mask=\$(echo "\$allowed" | cut -d'/' -f2)
            
            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 ! ipset list "\$IPSET_NAME" >/dev/null 2>&1; then
        modprobe xt_set 2>/dev/null
        modprobe ip_set 2>/dev/null
        modprobe ip_set_hash_ip 2>/dev/null
        
        if [[ "\$BAN_TIME" -gt 0 ]]; then
            ipset create "\$IPSET_NAME" hash:ip timeout \$BAN_TIME 2>/dev/null
        else
            ipset create "\$IPSET_NAME" hash:ip 2>/dev/null
        fi
    fi
    
    if ipset test "\$IPSET_NAME" "\$ip" 2>/dev/null; then
        log_ban "ℹ️  IP \$ip уже заблокирован"
        return 0
    fi
    
    if [[ "\$BAN_TIME" -gt 0 ]]; then
        if ipset add "\$IPSET_NAME" "\$ip" timeout \$BAN_TIME 2>/dev/null; then
            log_ban "✅ IP \$ip заблокирован на \$BAN_TIME секунд (\$((\$BAN_TIME / 3600)) часов) через Shorewall+ipset"
            return 0
        fi
    else
        if ipset add "\$IPSET_NAME" "\$ip" 2>/dev/null; then
            log_ban "✅ IP \$ip заблокирован НАВСЕГДА через Shorewall+ipset"
            return 0
        fi
    fi
    
    log_ban "❌ НЕ УДАЛОСЬ ЗАБЛОКИРОВАТЬ IP \$ip"
    return 1
}

unban_ip() {
    local ip="\$1"
    
    if ipset test "\$IPSET_NAME" "\$ip" 2>/dev/null; then
        ipset del "\$IPSET_NAME" "\$ip" 2>/dev/null
        log_ban "🔓 IP \$ip разблокирован вручную"
        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    
    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
Действие: Автоматическая блокировка через Shorewall + ipset"
        
        if [[ "\$BAN_TIME" -gt 0 ]]; then
            body+="
⏰ IP автоматически разблокируется через \$((\$BAN_TIME / 3600)) часов!"
        fi
        
        body+="

Текущие активные сессии:
\$(who -u 2>/dev/null || echo "Нет активных сессий")

Заблокированные IP (ipset):
\$(ipset list "\$IPSET_NAME" 2>/dev/null | grep -E "^[0-9]" | head -10 || echo "Нет блокировок")
========================================"
        send_email "\$subject" "\$body"
    fi
}

show_status() {
    echo "========================================="
    echo "SSH MONITOR WITH AUTO-BAN (Shorewall+IPSet)"
    echo "========================================="
    echo ""
    echo "Server: \$SERVER_NAME"
    echo "Emails (\${#EMAILS_TO[@]}):"
    for email in "\${EMAILS_TO[@]}"; do
        echo "  - \$email"
    done
    echo ""
    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 "IPSet: \$IPSET_NAME"
    echo "Shorewall: \$(shorewall version 2>/dev/null || echo 'не установлен')"
    echo ""
    echo "Blocked IPs (ipset):"
    if ipset list "\$IPSET_NAME" >/dev/null 2>&1; then
        local count=\$(ipset list "\$IPSET_NAME" | grep -cE "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+")
        echo "  Всего: \$count"
        ipset list "\$IPSET_NAME" | grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" | while read ip timeout; do
            if [[ -n "\$timeout" ]]; then
                echo "  - \$ip (осталось: \$((timeout / 60)) мин)"
            else
                echo "  - \$ip (навсегда)"
            fi
        done
    else
        echo "  ipset \$IPSET_NAME не создан"
    fi
    echo ""
    echo "Log: \$LOG_FILE"
    echo "Ban log: \$BAN_LOG"
    echo "========================================="
}

test_email() {
    echo "Sending test email to: \${EMAILS_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 (Shorewall+ipset) enabled.

Server: \$SERVER_NAME
IP: \$(hostname -I | awk '{print \$1}')
Time: \$(date '+%Y-%m-%d %H:%M:%S')
========================================"
    
    send_email "\$subject" "\$body"
    echo "✅ Test email process finished!"
}

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
        unban_ip "\$2"
        echo "✅ IP \$2 разблокирован"
        exit 0
        ;;
    --list|list)
        if ipset list "\$IPSET_NAME" >/dev/null 2>&1; then
            echo "Blocked IPs:"
            ipset list "\$IPSET_NAME" | grep -E "^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+"
        else
            echo "ipset \$IPSET_NAME не создан"
        fi
        exit 0
        ;;
    --help|help|-h)
        echo "Usage: \$0 [OPTION]"
        echo "  --status  - Show status"
        echo "  --test    - Send test email"
        echo "  --unban IP - Unblock IP"
        echo "  --list    - List blocked IPs"
        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"

if ! ipset list "\$IPSET_NAME" >/dev/null 2>&1; then
    modprobe xt_set 2>/dev/null
    modprobe ip_set 2>/dev/null
    modprobe ip_set_hash_ip 2>/dev/null
    
    if [[ "\$BAN_TIME" -gt 0 ]]; then
        ipset create "\$IPSET_NAME" hash:ip timeout \$BAN_TIME 2>/dev/null
    else
        ipset create "\$IPSET_NAME" hash:ip 2>/dev/null
    fi
fi

log_message "========================================="
log_message "🚀 START SSH MONITOR WITH AUTO-BAN (Shorewall+IPSet)"
log_message "Server: \$SERVER_NAME"
log_message "Emails: \${EMAILS_TO[*]}"
log_message "Allowed IPs: \${ALLOWED_IPS[*]}"
log_message "Ban time: \$BAN_TIME seconds"
log_message "Auto block: \$AUTO_BLOCK"
log_message "IPSet: \$IPSET_NAME"
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
SCRIPT_EOF

chmod +x /usr/local/bin/ssh-monitor.sh
print_success "Скрипт создан: /usr/local/bin/ssh-monitor.sh"

# ==================== 11. СОЗДАНИЕ СЕРВИСА ====================
print_header "📝 СОЗДАНИЕ СИСТЕМНОГО СЕРВИСА"

cat > /etc/systemd/system/ssh-monitor.service << 'EOF'
[Unit]
Description=SSH Monitor with Auto-Ban (Shorewall+IPSet)
After=network.target shorewall.service
Wants=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/ssh-monitor.sh
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable ssh-monitor
systemctl restart ssh-monitor

print_success "Сервис создан и запущен"

# ==================== 12. ТЕСТ ====================
print_header "🧪 ТЕСТИРОВАНИЕ"

sleep 2

if systemctl is-active --quiet ssh-monitor; then
    print_success "Сервис работает!"
else
    print_error "Сервис не запущен"
    systemctl status ssh-monitor --no-pager
fi

echo ""
print_question "Отправить тестовое письмо на все адреса?"
echo -e "${YELLOW}Получатели: ${EMAILS_TO[*]}${NC}"
read -p "(y/n): " -n 1 -r
echo ""
if [[ $REPLY =~ ^[Yy]$ ]]; then
    /usr/local/bin/ssh-monitor.sh --test
    echo ""
fi

# ==================== 13. ИНФОРМАЦИЯ ====================
print_header "✅ УСТАНОВКА ЗАВЕРШЕНА!"

echo -e "${CYAN}📋 ВАШИ НАСТРОЙКИ:${NC}"
if [[ -n "$SMTP_HOST" ]]; then
    echo -e "  📧 SMTP: ${GREEN}$SMTP_HOST:$SMTP_PORT${NC}"
    echo -e "  👤 SMTP логин: ${GREEN}$SMTP_USER${NC}"
fi
echo -e "  🔥 Shorewall: ${GREEN}$(shorewall version)${NC}"
echo -e "  🏷️  Имя сервера: ${GREEN}$SERVER_NAME${NC}"
echo -e "  📧 Email (${#EMAILS_TO[@]}):"
for email in "${EMAILS_TO[@]}"; do
    echo -e "     • ${GREEN}$email${NC}"
done
echo -e "  🛡️ Разрешенные IP (${#ALLOWED_IPS[@]}):"
for ip in "${ALLOWED_IPS[@]}"; do
    echo -e "     • ${GREEN}$ip${NC}"
done
echo -e "  🔒 Время блокировки: ${GREEN}$BAN_TEXT${NC}"
echo -e "  ⏰ Авторазбан: ${GREEN}ДА${NC} (через $BAN_TEXT)"
echo -e "  ⚡ Действие: ${GREEN}$ACTION_TEXT${NC}"
echo -e "  📦 IPSet: ${GREEN}$IPSET_NAME${NC}"
echo ""

echo -e "${CYAN}📝 КОМАНДЫ УПРАВЛЕНИЯ:${NC}"
echo -e "  ${BLUE}systemctl status ssh-monitor${NC}   - Статус"
echo -e "  ${BLUE}systemctl restart ssh-monitor${NC}  - Перезапуск"
echo -e "  ${BLUE}tail -f /var/log/ssh-monitor.log${NC} - Логи"
echo -e "  ${BLUE}tail -f /var/log/ssh-ban.log${NC}   - Лог блокировок"
echo -e "  ${BLUE}tail -f /var/log/msmtp.log${NC}     - Логи почты"
echo ""
echo -e "  ${BLUE}/usr/local/bin/ssh-monitor.sh --test${NC}    - Тест почты"
echo -e "  ${BLUE}/usr/local/bin/ssh-monitor.sh --status${NC}  - Статус"
echo -e "  ${BLUE}/usr/local/bin/ssh-monitor.sh --list${NC}    - Список банов"
echo -e "  ${BLUE}/usr/local/bin/ssh-monitor.sh --unban IP${NC} - Разблокировать"
echo ""

echo -e "${CYAN}🔍 ПРОВЕРКА:${NC}"
echo -e "  ${BLUE}ipset list $IPSET_NAME${NC}              - Заблокированные IP"
echo -e "  ${BLUE}iptables -n -L net-fw | grep $IPSET_NAME${NC} - Правило"
echo -e "  ${BLUE}shorewall status${NC}                    - Статус Shorewall"
echo ""

print_success "SSH Monitor с автобаном успешно установлен!"
echo -e "${GREEN}Система готова к работе!${NC}"
echo ""

exit 0
КАК ИСПОЛЬЗОВАТЬ:


# 1. Создайте файл

Код: Выделить всё

vim install-ssh-monitor.sh
# 2. Скопируйте ВЕСЬ код выше

# 3. Сделайте исполняемым

Код: Выделить всё

chmod +x install-ssh-monitor.sh
# 4. Запустите

Код: Выделить всё

./install-ssh-monitor.sh
📝 ЧТО СПРАШИВАЕТ УСТАНОВЩИК:

Почта — настроить msmtp / использовать существующую / пропустить

SMTP настройки — сервер, порт, шифрование, логин, пароль

Имя сервера — для уведомлений

Email — можно несколько через запятую

Разрешенные IP — одной строкой через пробел

Время блокировки — 1ч / 24ч / 7д / 30д

Действие — блокировка + уведомление / только блокировка / только уведомление

✅ ОСОБЕННОСТИ ФИНАЛЬНОЙ ВЕРСИИ:

✅ Автоустановка зависимостей (ipset, msmtp)

✅ Интеграция с Shorewall — через initdone и net:+ssh_ban

✅ Автоматический разбан через ipset timeout

✅ Поддержка подсетей (/22, /24, /16, /8)

✅ Несколько email для уведомлений

✅ Ввод IP одной строкой

✅ Правильный Perl синтаксис для initdone

✅ Правило net:+ssh_ban (с плюсом!)

✅ Тест почты во время установки

✅ Проверка конфига Shorewall перед применением

✅ Сохранение правил после shorewall restart

✅ Модули ядра в автозагрузке

🎯 ПРОВЕРЕНО НА:

✅ Ubuntu 26.04 LTS

✅ Shorewall 5.2.8

✅ IPSet v7.19

✅ msmtp

ГОТОВЫЕ КОМАНДЫ ДЛЯ УПРАВЛЕНИЯ:

# === Управление сервисом ===

Код: Выделить всё

systemctl status ssh-monitor
systemctl restart ssh-monitor
systemctl stop ssh-monitor
systemctl start ssh-monitor
# === Статус и баны ===

Код: Выделить всё

/usr/local/bin/ssh-monitor.sh --status
/usr/local/bin/ssh-monitor.sh --list
/usr/local/bin/ssh-monitor.sh --test
# === Разблокировка ===

Код: Выделить всё

/usr/local/bin/ssh-monitor.sh --unban 8.8.8.8
# === Логи ===

Код: Выделить всё

tail -f /var/log/ssh-monitor.log
tail -f /var/log/ssh-ban.log
tail -f /var/log/msmtp.log
# === Проверка состояния ===

Код: Выделить всё

ipset list ssh_ban
iptables -n -L net-fw | grep ssh_ban
shorewall status