#!/usr/bin/env python3 """ PyGuardian - Linux Server Protection System Главный файл для запуска системы мониторинга и защиты Автор: SmartSolTech Team Лицензия: MIT """ import asyncio import signal import logging import logging.handlers import yaml import sys import os from pathlib import Path from typing import Dict, Optional # Добавляем src в путь для импортов sys.path.insert(0, str(Path(__file__).parent / "src")) from src.storage import Storage from src.firewall import FirewallManager from src.monitor import LogMonitor, AttackDetector from src.bot import TelegramBot, NotificationManager from src.security import SecurityManager from src.sessions import SessionManager from src.password_utils import PasswordManager class PyGuardian: """Главный класс системы PyGuardian""" def __init__(self, config_path: str = None): self.config_path = config_path or "config/config.yaml" self.config: Optional[Dict] = None self.logger = None # Компоненты системы self.storage: Optional[Storage] = None self.firewall_manager: Optional[FirewallManager] = None self.log_monitor: Optional[LogMonitor] = None self.attack_detector: Optional[AttackDetector] = None self.telegram_bot: Optional[TelegramBot] = None self.notification_manager: Optional[NotificationManager] = None # Новые компоненты безопасности self.security_manager: Optional[SecurityManager] = None self.session_manager: Optional[SessionManager] = None self.password_manager: Optional[PasswordManager] = None # Флаги состояния self.running = False self.shutdown_event = asyncio.Event() # Задачи self.monitor_task: Optional[asyncio.Task] = None self.bot_task: Optional[asyncio.Task] = None self.cleanup_task: Optional[asyncio.Task] = None self.unban_checker_task: Optional[asyncio.Task] = None def setup_logging(self) -> None: """Настройка логирования""" log_config = self.config.get('logging', {}) log_file = log_config.get('log_file', '/var/log/pyguardian.log') log_level = getattr(logging, log_config.get('log_level', 'INFO').upper()) max_log_size = log_config.get('max_log_size', 10485760) # 10MB backup_count = log_config.get('backup_count', 5) # Создаем директорию для логов если не существует os.makedirs(os.path.dirname(log_file), exist_ok=True) # Настройка форматирования formatter = logging.Formatter( '%(asctime)s - %(name)s - %(levelname)s - %(message)s' ) # Ротируемый файловый обработчик file_handler = logging.handlers.RotatingFileHandler( log_file, maxBytes=max_log_size, backupCount=backup_count ) file_handler.setFormatter(formatter) # Консольный обработчик console_handler = logging.StreamHandler() console_handler.setFormatter(formatter) # Настройка root logger logging.basicConfig( level=log_level, handlers=[file_handler, console_handler] ) self.logger = logging.getLogger('PyGuardian') self.logger.info("Логирование настроено успешно") def load_config(self) -> bool: """Загрузка конфигурации""" try: with open(self.config_path, 'r', encoding='utf-8') as file: self.config = yaml.safe_load(file) # Валидация основных параметров required_keys = ['telegram', 'security', 'monitoring', 'firewall', 'storage'] for key in required_keys: if key not in self.config: raise ValueError(f"Отсутствует секция '{key}' в конфигурации") # Проверяем обязательные параметры Telegram telegram_config = self.config['telegram'] if not telegram_config.get('bot_token') or not telegram_config.get('admin_id'): raise ValueError("Не указаны bot_token или admin_id в секции telegram") self.logger.info("Конфигурация загружена успешно") return True except FileNotFoundError: print(f"Файл конфигурации не найден: {self.config_path}") return False except yaml.YAMLError as e: print(f"Ошибка парсинга YAML: {e}") return False except Exception as e: print(f"Ошибка загрузки конфигурации: {e}") return False async def initialize_components(self) -> bool: """Инициализация всех компонентов системы""" try: self.logger.info("Инициализация компонентов...") # Создаем директории storage_path = self.config['storage']['database_path'] os.makedirs(os.path.dirname(storage_path), exist_ok=True) # 1. Инициализация хранилища self.storage = Storage(storage_path) await self.storage.init_database() self.logger.info("Storage инициализирован") # 2. Инициализация firewall self.firewall_manager = FirewallManager(self.config['firewall']) if not await self.firewall_manager.setup(): raise RuntimeError("Не удалось настроить firewall") self.logger.info("Firewall Manager инициализирован") # 3. Инициализация новых менеджеров безопасности self.password_manager = PasswordManager() self.session_manager = SessionManager() self.security_manager = SecurityManager( self.storage, self.firewall_manager, self.config.get('security', {}) ) self.logger.info("Менеджеры безопасности инициализированы") # 4. Инициализация детектора атак с security manager attack_config = { **self.config['security'], 'whitelist': self.config.get('whitelist', []) } self.attack_detector = AttackDetector( self.storage, self.firewall_manager, self.security_manager, attack_config ) self.logger.info("Attack Detector инициализирован") # 5. Инициализация Telegram бота с новыми менеджерами self.telegram_bot = TelegramBot( self.config['telegram'], self.storage, self.firewall_manager, self.attack_detector, self.security_manager, self.session_manager, self.password_manager ) self.logger.info("Telegram Bot инициализирован") # 5. Инициализация менеджера уведомлений self.notification_manager = NotificationManager(self.telegram_bot) # Связываем detector с уведомлениями self.attack_detector.set_callbacks( ban_callback=self.notification_manager.on_ip_banned, unban_callback=self.notification_manager.on_ip_unbanned ) # 6. Инициализация монитора логов self.log_monitor = LogMonitor( self.config['monitoring'], event_callback=self.attack_detector.process_event ) self.logger.info("Log Monitor инициализирован") self.logger.info("Все компоненты инициализированы успешно") return True except Exception as e: self.logger.error(f"Ошибка инициализации компонентов: {e}") return False async def start_background_tasks(self) -> None: """Запуск фоновых задач""" try: self.logger.info("Запуск фоновых задач...") # 1. Задача мониторинга логов self.monitor_task = asyncio.create_task( self.log_monitor.start(), name="log_monitor" ) # 2. Задача Telegram бота self.bot_task = asyncio.create_task( self.telegram_bot.start_bot(), name="telegram_bot" ) # 3. Задача периодической очистки self.cleanup_task = asyncio.create_task( self.periodic_cleanup(), name="periodic_cleanup" ) # 4. Задача проверки истекших банов self.unban_checker_task = asyncio.create_task( self.periodic_unban_check(), name="unban_checker" ) self.logger.info("Фоновые задачи запущены") except Exception as e: self.logger.error(f"Ошибка запуска фоновых задач: {e}") raise async def periodic_cleanup(self) -> None: """Периодическая очистка данных""" cleanup_interval = self.config.get('performance', {}).get('cleanup_interval', 3600) max_records_age = self.config.get('performance', {}).get('max_records_age', 604800) while self.running: try: await asyncio.sleep(cleanup_interval) if not self.running: break # Очистка старых записей deleted_count = await self.storage.cleanup_old_records( days=max_records_age // 86400 ) # Обновление статистики await self.storage.update_daily_stats() # Очистка firewall от устаревших банов valid_ips = [ban['ip'] for ban in await self.storage.get_banned_ips()] removed_count = await self.firewall_manager.cleanup_expired_bans(valid_ips) if deleted_count > 0 or removed_count > 0: self.logger.info(f"Очистка: удалено {deleted_count} записей, {removed_count} правил firewall") except Exception as e: self.logger.error(f"Ошибка в periodic_cleanup: {e}") await asyncio.sleep(60) # Ждем минуту перед повтором async def periodic_unban_check(self) -> None: """Периодическая проверка истекших банов""" check_interval = 300 # Проверяем каждые 5 минут while self.running: try: await asyncio.sleep(check_interval) if not self.running: break await self.attack_detector.check_expired_bans() except Exception as e: self.logger.error(f"Ошибка в periodic_unban_check: {e}") await asyncio.sleep(60) # Ждем минуту перед повтором def setup_signal_handlers(self) -> None: """Настройка обработчиков сигналов""" def signal_handler(signum, frame): self.logger.info(f"Получен сигнал {signum}") asyncio.create_task(self.shutdown()) signal.signal(signal.SIGINT, signal_handler) signal.signal(signal.SIGTERM, signal_handler) if hasattr(signal, 'SIGHUP'): signal.signal(signal.SIGHUP, signal_handler) async def shutdown(self) -> None: """Graceful shutdown""" if not self.running: return self.logger.info("Начало graceful shutdown...") self.running = False try: # Останавливаем мониторинг логов if self.log_monitor: await self.log_monitor.stop() # Отменяем фоновые задачи tasks_to_cancel = [ self.monitor_task, self.cleanup_task, self.unban_checker_task ] for task in tasks_to_cancel: if task and not task.done(): task.cancel() try: await task except asyncio.CancelledError: pass # Останавливаем Telegram бота if self.telegram_bot: await self.telegram_bot.stop_bot() # Отменяем задачу бота отдельно if self.bot_task and not self.bot_task.done(): self.bot_task.cancel() try: await self.bot_task except asyncio.CancelledError: pass self.logger.info("Graceful shutdown завершен") except Exception as e: self.logger.error(f"Ошибка при shutdown: {e}") finally: self.shutdown_event.set() async def run(self) -> None: """Основной цикл работы""" try: # Загрузка конфигурации if not self.load_config(): return # Настройка логирования self.setup_logging() # Настройка обработчиков сигналов self.setup_signal_handlers() # Инициализация компонентов if not await self.initialize_components(): self.logger.error("Не удалось инициализировать компоненты") return # Установка флага работы self.running = True # Запуск фоновых задач await self.start_background_tasks() self.logger.info("PyGuardian запущен и готов к работе") # Ожидание сигнала к остановке await self.shutdown_event.wait() except KeyboardInterrupt: self.logger.info("Получен KeyboardInterrupt") except Exception as e: self.logger.error(f"Критическая ошибка: {e}") if self.notification_manager: await self.notification_manager.on_system_error(str(e)) finally: await self.shutdown() async def main(): """Главная функция""" # Проверяем аргументы командной строки config_path = None if len(sys.argv) > 1: config_path = sys.argv[1] # Проверяем права root (для работы с iptables/nftables) if os.geteuid() != 0: print("⚠️ Предупреждение: PyGuardian рекомендуется запускать от root для работы с firewall") # Создаем и запускаем PyGuardian guardian = PyGuardian(config_path) await guardian.run() if __name__ == "__main__": try: asyncio.run(main()) except KeyboardInterrupt: print("\nПрерывание пользователем") except Exception as e: print(f"Фатальная ошибка: {e}") sys.exit(1)