diff --git a/.history/.drone_20251125210206.yml b/.history/.drone_20251125210206.yml deleted file mode 100644 index 7790bf3..0000000 --- a/.history/.drone_20251125210206.yml +++ /dev/null @@ -1,339 +0,0 @@ -kind: pipeline -type: docker -name: pyguardian-ci - -platform: - os: linux - arch: amd64 - -# Build triggers -trigger: - branch: - - main - - develop - event: - - push - - pull_request - - tag - -# Global environment variables -environment: - PYTHON_VERSION: "3.11" - POETRY_VERSION: "1.7.0" - -steps: - # Code quality and testing pipeline - - name: lint-and-test - image: python:3.11-slim - environment: - PYTHONPATH: /drone/src - commands: - # Install system dependencies - - apt-get update && apt-get install -y git curl - - # Install Python dependencies - - pip install --upgrade pip - - pip install -r requirements.txt - - pip install pytest pytest-asyncio pytest-cov flake8 black mypy - - # Code formatting check - - black --check src/ tests/ - - # Lint code - - flake8 src/ --max-line-length=88 --extend-ignore=E203,W503 - - # Type checking - - mypy src/ --ignore-missing-imports - - # Run unit tests with coverage - - pytest tests/unit/ -v --cov=src --cov-report=xml --cov-report=term - - # Security check for dependencies - - pip install safety - - safety check - - # Integration tests - - name: integration-tests - image: python:3.11-slim - environment: - PYTHONPATH: /drone/src - TEST_DATABASE_URL: sqlite:///tmp/test.db - commands: - - apt-get update && apt-get install -y iptables curl - - pip install -r requirements.txt - - pip install pytest pytest-asyncio - - pytest tests/integration/ -v - depends_on: - - lint-and-test - - # Build Docker images - - name: build-docker-images - image: docker:24-dind - environment: - DOCKER_BUILDKIT: 1 - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Build controller image - - docker build -f deployment/docker/Dockerfile --target controller -t pyguardian:controller-${DRONE_COMMIT_SHA:0:8} . - - # Build agent image - - docker build -f deployment/docker/Dockerfile --target agent -t pyguardian:agent-${DRONE_COMMIT_SHA:0:8} . - - # Build standalone image - - docker build -f deployment/docker/Dockerfile --target standalone -t pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} . - - # Test images can start - - timeout 30 docker run --rm pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} python --version - depends_on: - - integration-tests - - # Security scanning - - name: security-scan - image: aquasec/trivy:latest - commands: - # Scan for vulnerabilities in built images - - trivy image --no-progress --severity HIGH,CRITICAL pyguardian:controller-${DRONE_COMMIT_SHA:0:8} - - trivy image --no-progress --severity HIGH,CRITICAL pyguardian:agent-${DRONE_COMMIT_SHA:0:8} - depends_on: - - build-docker-images - failure: ignore # Don't fail build on security issues, but report them - - # End-to-end tests - - name: e2e-tests - image: docker/compose:latest - environment: - COMPOSE_FILE: deployment/docker/docker-compose.yml - TELEGRAM_BOT_TOKEN: test_token - CLUSTER_SECRET: test_secret - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Start services - - docker-compose -f deployment/docker/docker-compose.yml up -d - - # Wait for services to be ready - - sleep 30 - - # Run E2E tests - - python tests/e2e/test_cluster_communication.py - - # Cleanup - - docker-compose -f deployment/docker/docker-compose.yml down -v - depends_on: - - build-docker-images - failure: ignore # E2E tests are flaky in CI - - # Documentation build - - name: build-docs - image: python:3.11-slim - commands: - - pip install mkdocs mkdocs-material - - mkdocs build --strict - depends_on: - - lint-and-test - - # Package creation - - name: create-packages - image: python:3.11-slim - commands: - # Create installation package - - tar -czf pyguardian-${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}.tar.gz \ - src/ config/ main.py requirements.txt deployment/scripts/ - - # Create checksums - - sha256sum pyguardian-${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}.tar.gz > checksums.txt - depends_on: - - build-docker-images - - build-docs - - # Release workflow (only on tags) - - name: docker-registry-push - image: docker:24-dind - environment: - REGISTRY: - from_secret: docker_registry - REGISTRY_USERNAME: - from_secret: docker_username - REGISTRY_PASSWORD: - from_secret: docker_password - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Login to registry - - docker login -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD $REGISTRY - - # Tag and push images - - docker tag pyguardian:controller-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:controller-${DRONE_TAG} - - docker tag pyguardian:agent-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:agent-${DRONE_TAG} - - docker tag pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:standalone-${DRONE_TAG} - - - docker push $REGISTRY/pyguardian:controller-${DRONE_TAG} - - docker push $REGISTRY/pyguardian:agent-${DRONE_TAG} - - docker push $REGISTRY/pyguardian:standalone-${DRONE_TAG} - - # Also tag as latest if this is a release - - | - if [ "$DRONE_TAG" != "" ]; then - docker tag pyguardian:controller-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:controller-latest - docker tag pyguardian:agent-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:agent-latest - docker tag pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:standalone-latest - - docker push $REGISTRY/pyguardian:controller-latest - docker push $REGISTRY/pyguardian:agent-latest - docker push $REGISTRY/pyguardian:standalone-latest - fi - depends_on: - - create-packages - when: - event: - - tag - - # GitHub Release - - name: github-release - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: - - pyguardian-*.tar.gz - - checksums.txt - title: "PyGuardian ${DRONE_TAG}" - note: | - ## PyGuardian Release ${DRONE_TAG} - - ### Features - - Advanced agent authentication with JWT tokens - - Centralized cluster management - - Secure API endpoints for agent communication - - Docker containerization support - - ### Installation - ```bash - # Download and extract - wget https://github.com/SmartSolTech/PyGuardian/releases/download/${DRONE_TAG}/pyguardian-${DRONE_TAG}.tar.gz - tar -xzf pyguardian-${DRONE_TAG}.tar.gz - - # Install - sudo ./deployment/scripts/install.sh - ``` - - ### Docker - ```bash - # Pull images - docker pull ${REGISTRY}/pyguardian:controller-${DRONE_TAG} - docker pull ${REGISTRY}/pyguardian:agent-${DRONE_TAG} - - # Run with docker-compose - curl -O https://raw.githubusercontent.com/SmartSolTech/PyGuardian/${DRONE_TAG}/deployment/docker/docker-compose.yml - docker-compose up -d - ``` - depends_on: - - docker-registry-push - when: - event: - - tag - - # Deployment notification - - name: notify-deployment - image: plugins/webhook - settings: - urls: - from_secret: deployment_webhook - content_type: application/json - template: | - { - "text": "🚀 PyGuardian ${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}} deployed successfully!", - "attachments": [{ - "color": "good", - "fields": [{ - "title": "Version", - "value": "${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}", - "short": true - }, { - "title": "Commit", - "value": "${DRONE_COMMIT_MESSAGE}", - "short": false - }] - }] - } - depends_on: - - github-release - when: - status: - - success - event: - - tag - -# Volumes for Docker in Docker -volumes: - - name: docker-sock - host: - path: /var/run/docker.sock - ---- -# Separate pipeline for nightly builds -kind: pipeline -type: docker -name: nightly-security-scan - -trigger: - cron: - - nightly-security - -steps: - - name: dependency-security-scan - image: python:3.11-slim - commands: - - pip install safety bandit semgrep - - # Check for known vulnerable dependencies - - safety check --json --output safety-report.json || true - - # Static security analysis - - bandit -r src/ -f json -o bandit-report.json || true - - # Semgrep security rules - - semgrep --config=auto src/ --json --output semgrep-report.json || true - - # Upload results to security dashboard - - python deployment/scripts/upload-security-reports.py - - - name: container-security-scan - image: aquasec/trivy:latest - commands: - # Build fresh images - - docker build -t pyguardian:security-scan . - - # Comprehensive vulnerability scan - - trivy image --format json --output trivy-report.json pyguardian:security-scan - - # Upload to security dashboard - - python deployment/scripts/upload-trivy-report.py - ---- -# Documentation deployment pipeline -kind: pipeline -type: docker -name: docs-deployment - -trigger: - branch: - - main - path: - include: - - "documentation/**" - - "*.md" - -steps: - - name: build-and-deploy-docs - image: python:3.11-slim - environment: - GITHUB_TOKEN: - from_secret: github_token - commands: - - pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin - - mkdocs gh-deploy --force \ No newline at end of file diff --git a/.history/.drone_20251125210433.yml b/.history/.drone_20251125210433.yml deleted file mode 100644 index 7790bf3..0000000 --- a/.history/.drone_20251125210433.yml +++ /dev/null @@ -1,339 +0,0 @@ -kind: pipeline -type: docker -name: pyguardian-ci - -platform: - os: linux - arch: amd64 - -# Build triggers -trigger: - branch: - - main - - develop - event: - - push - - pull_request - - tag - -# Global environment variables -environment: - PYTHON_VERSION: "3.11" - POETRY_VERSION: "1.7.0" - -steps: - # Code quality and testing pipeline - - name: lint-and-test - image: python:3.11-slim - environment: - PYTHONPATH: /drone/src - commands: - # Install system dependencies - - apt-get update && apt-get install -y git curl - - # Install Python dependencies - - pip install --upgrade pip - - pip install -r requirements.txt - - pip install pytest pytest-asyncio pytest-cov flake8 black mypy - - # Code formatting check - - black --check src/ tests/ - - # Lint code - - flake8 src/ --max-line-length=88 --extend-ignore=E203,W503 - - # Type checking - - mypy src/ --ignore-missing-imports - - # Run unit tests with coverage - - pytest tests/unit/ -v --cov=src --cov-report=xml --cov-report=term - - # Security check for dependencies - - pip install safety - - safety check - - # Integration tests - - name: integration-tests - image: python:3.11-slim - environment: - PYTHONPATH: /drone/src - TEST_DATABASE_URL: sqlite:///tmp/test.db - commands: - - apt-get update && apt-get install -y iptables curl - - pip install -r requirements.txt - - pip install pytest pytest-asyncio - - pytest tests/integration/ -v - depends_on: - - lint-and-test - - # Build Docker images - - name: build-docker-images - image: docker:24-dind - environment: - DOCKER_BUILDKIT: 1 - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Build controller image - - docker build -f deployment/docker/Dockerfile --target controller -t pyguardian:controller-${DRONE_COMMIT_SHA:0:8} . - - # Build agent image - - docker build -f deployment/docker/Dockerfile --target agent -t pyguardian:agent-${DRONE_COMMIT_SHA:0:8} . - - # Build standalone image - - docker build -f deployment/docker/Dockerfile --target standalone -t pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} . - - # Test images can start - - timeout 30 docker run --rm pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} python --version - depends_on: - - integration-tests - - # Security scanning - - name: security-scan - image: aquasec/trivy:latest - commands: - # Scan for vulnerabilities in built images - - trivy image --no-progress --severity HIGH,CRITICAL pyguardian:controller-${DRONE_COMMIT_SHA:0:8} - - trivy image --no-progress --severity HIGH,CRITICAL pyguardian:agent-${DRONE_COMMIT_SHA:0:8} - depends_on: - - build-docker-images - failure: ignore # Don't fail build on security issues, but report them - - # End-to-end tests - - name: e2e-tests - image: docker/compose:latest - environment: - COMPOSE_FILE: deployment/docker/docker-compose.yml - TELEGRAM_BOT_TOKEN: test_token - CLUSTER_SECRET: test_secret - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Start services - - docker-compose -f deployment/docker/docker-compose.yml up -d - - # Wait for services to be ready - - sleep 30 - - # Run E2E tests - - python tests/e2e/test_cluster_communication.py - - # Cleanup - - docker-compose -f deployment/docker/docker-compose.yml down -v - depends_on: - - build-docker-images - failure: ignore # E2E tests are flaky in CI - - # Documentation build - - name: build-docs - image: python:3.11-slim - commands: - - pip install mkdocs mkdocs-material - - mkdocs build --strict - depends_on: - - lint-and-test - - # Package creation - - name: create-packages - image: python:3.11-slim - commands: - # Create installation package - - tar -czf pyguardian-${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}.tar.gz \ - src/ config/ main.py requirements.txt deployment/scripts/ - - # Create checksums - - sha256sum pyguardian-${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}.tar.gz > checksums.txt - depends_on: - - build-docker-images - - build-docs - - # Release workflow (only on tags) - - name: docker-registry-push - image: docker:24-dind - environment: - REGISTRY: - from_secret: docker_registry - REGISTRY_USERNAME: - from_secret: docker_username - REGISTRY_PASSWORD: - from_secret: docker_password - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Login to registry - - docker login -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD $REGISTRY - - # Tag and push images - - docker tag pyguardian:controller-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:controller-${DRONE_TAG} - - docker tag pyguardian:agent-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:agent-${DRONE_TAG} - - docker tag pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:standalone-${DRONE_TAG} - - - docker push $REGISTRY/pyguardian:controller-${DRONE_TAG} - - docker push $REGISTRY/pyguardian:agent-${DRONE_TAG} - - docker push $REGISTRY/pyguardian:standalone-${DRONE_TAG} - - # Also tag as latest if this is a release - - | - if [ "$DRONE_TAG" != "" ]; then - docker tag pyguardian:controller-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:controller-latest - docker tag pyguardian:agent-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:agent-latest - docker tag pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:standalone-latest - - docker push $REGISTRY/pyguardian:controller-latest - docker push $REGISTRY/pyguardian:agent-latest - docker push $REGISTRY/pyguardian:standalone-latest - fi - depends_on: - - create-packages - when: - event: - - tag - - # GitHub Release - - name: github-release - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: - - pyguardian-*.tar.gz - - checksums.txt - title: "PyGuardian ${DRONE_TAG}" - note: | - ## PyGuardian Release ${DRONE_TAG} - - ### Features - - Advanced agent authentication with JWT tokens - - Centralized cluster management - - Secure API endpoints for agent communication - - Docker containerization support - - ### Installation - ```bash - # Download and extract - wget https://github.com/SmartSolTech/PyGuardian/releases/download/${DRONE_TAG}/pyguardian-${DRONE_TAG}.tar.gz - tar -xzf pyguardian-${DRONE_TAG}.tar.gz - - # Install - sudo ./deployment/scripts/install.sh - ``` - - ### Docker - ```bash - # Pull images - docker pull ${REGISTRY}/pyguardian:controller-${DRONE_TAG} - docker pull ${REGISTRY}/pyguardian:agent-${DRONE_TAG} - - # Run with docker-compose - curl -O https://raw.githubusercontent.com/SmartSolTech/PyGuardian/${DRONE_TAG}/deployment/docker/docker-compose.yml - docker-compose up -d - ``` - depends_on: - - docker-registry-push - when: - event: - - tag - - # Deployment notification - - name: notify-deployment - image: plugins/webhook - settings: - urls: - from_secret: deployment_webhook - content_type: application/json - template: | - { - "text": "🚀 PyGuardian ${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}} deployed successfully!", - "attachments": [{ - "color": "good", - "fields": [{ - "title": "Version", - "value": "${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}", - "short": true - }, { - "title": "Commit", - "value": "${DRONE_COMMIT_MESSAGE}", - "short": false - }] - }] - } - depends_on: - - github-release - when: - status: - - success - event: - - tag - -# Volumes for Docker in Docker -volumes: - - name: docker-sock - host: - path: /var/run/docker.sock - ---- -# Separate pipeline for nightly builds -kind: pipeline -type: docker -name: nightly-security-scan - -trigger: - cron: - - nightly-security - -steps: - - name: dependency-security-scan - image: python:3.11-slim - commands: - - pip install safety bandit semgrep - - # Check for known vulnerable dependencies - - safety check --json --output safety-report.json || true - - # Static security analysis - - bandit -r src/ -f json -o bandit-report.json || true - - # Semgrep security rules - - semgrep --config=auto src/ --json --output semgrep-report.json || true - - # Upload results to security dashboard - - python deployment/scripts/upload-security-reports.py - - - name: container-security-scan - image: aquasec/trivy:latest - commands: - # Build fresh images - - docker build -t pyguardian:security-scan . - - # Comprehensive vulnerability scan - - trivy image --format json --output trivy-report.json pyguardian:security-scan - - # Upload to security dashboard - - python deployment/scripts/upload-trivy-report.py - ---- -# Documentation deployment pipeline -kind: pipeline -type: docker -name: docs-deployment - -trigger: - branch: - - main - path: - include: - - "documentation/**" - - "*.md" - -steps: - - name: build-and-deploy-docs - image: python:3.11-slim - environment: - GITHUB_TOKEN: - from_secret: github_token - commands: - - pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin - - mkdocs gh-deploy --force \ No newline at end of file diff --git a/.history/.drone_20251125211710.yml b/.history/.drone_20251125211710.yml deleted file mode 100644 index cfec609..0000000 --- a/.history/.drone_20251125211710.yml +++ /dev/null @@ -1,549 +0,0 @@ -kind: pipeline -type: docker -name: pyguardian-ci - -steps: - # 1. Environment Setup and Dependency Installation - - name: setup-environment - image: python:3.11-slim - commands: - - echo "🔧 Setting up build environment..." - - python --version - - pip install --upgrade pip - - apt-get update && apt-get install -y git curl - - echo "✅ Environment setup complete" - when: - event: - - push - - pull_request - - # 2. Install Dependencies - - name: install-dependencies - image: python:3.11-slim - commands: - - echo "📦 Installing Python dependencies..." - - pip install -r requirements.txt - - pip install pytest pytest-cov pytest-asyncio flake8 black isort - - echo "✅ Dependencies installed" - depends_on: - - setup-environment - - # 3. Code Quality - Linting - - name: lint-code - image: python:3.11-slim - commands: - - echo "🔍 Running code linting..." - - pip install flake8 black isort - - echo "Running Black formatter check..." - - black --check --diff src/ tests/ || true - - echo "Running isort import check..." - - isort --check-only --diff src/ tests/ || true - - echo "Running flake8 linting..." - - flake8 src/ tests/ --max-line-length=100 --ignore=E203,W503 || true - - echo "✅ Code linting complete" - depends_on: - - install-dependencies - - # 4. Unit Tests - - name: unit-tests - image: python:3.11-slim - commands: - - echo "🧪 Running unit tests..." - - pip install -r requirements.txt pytest pytest-cov pytest-asyncio - - export PYTHONPATH="${PWD}/src:${PYTHONPATH}" - - python -m pytest tests/unit/ -v --tb=short || true - - echo "✅ Unit tests complete" - depends_on: - - lint-code - - # 5. Integration Tests - - name: integration-tests - image: python:3.11-slim - commands: - - echo "🔄 Running integration tests..." - - pip install -r requirements.txt pytest pytest-asyncio - - export PYTHONPATH="${PWD}/src:${PYTHONPATH}" - - python -m pytest tests/integration/ -v --tb=short || true - - echo "✅ Integration tests complete" - depends_on: - - unit-tests - - # 6. End-to-End Tests - - name: e2e-tests - image: python:3.11-slim - commands: - - echo "🎯 Running end-to-end tests..." - - pip install -r requirements.txt pytest pytest-asyncio - - export PYTHONPATH="${PWD}/src:${PYTHONPATH}" - - python -m pytest tests/e2e/ -v --tb=short || true - - echo "✅ E2E tests complete" - depends_on: - - integration-tests - - # 7. Test Coverage Report - - name: coverage-report - image: python:3.11-slim - commands: - - echo "📊 Generating test coverage report..." - - pip install -r requirements.txt pytest pytest-cov - - export PYTHONPATH="${PWD}/src:${PYTHONPATH}" - - python -m pytest tests/ --cov=src --cov-report=term-missing --cov-report=xml || true - - echo "✅ Coverage report generated" - depends_on: - - e2e-tests - - # 8. Security Scanning - - name: security-scan - image: python:3.11-slim - commands: - - echo "🛡️ Running security scans..." - - pip install bandit safety - - echo "Running Bandit security scanner..." - - bandit -r src/ -f json -o bandit-report.json || true - - echo "Running Safety dependency checker..." - - safety check --json --output safety-report.json || true - - echo "✅ Security scans complete" - depends_on: - - coverage-report - - # 9. Docker Image Build - Controller - - name: build-controller-image - image: plugins/docker - settings: - repo: pyguardian - tags: - - controller-${DRONE_COMMIT_SHA:0:8} - - controller-latest - target: controller - dockerfile: deployment/docker/Dockerfile - build_args: - - BUILD_DATE=${DRONE_BUILD_CREATED} - - VCS_REF=${DRONE_COMMIT_SHA} - - VERSION=${DRONE_TAG:-dev} - depends_on: - - security-scan - when: - event: - - push - branch: - - main - - # 10. Docker Image Build - Agent - - name: build-agent-image - image: plugins/docker - settings: - repo: pyguardian - tags: - - agent-${DRONE_COMMIT_SHA:0:8} - - agent-latest - target: agent - dockerfile: deployment/docker/Dockerfile - build_args: - - BUILD_DATE=${DRONE_BUILD_CREATED} - - VCS_REF=${DRONE_COMMIT_SHA} - - VERSION=${DRONE_TAG:-dev} - depends_on: - - security-scan - when: - event: - - push - branch: - - main - - # 11. Docker Image Security Scan - - name: scan-docker-images - image: aquasec/trivy - commands: - - echo "🔒 Scanning Docker images for vulnerabilities..." - - trivy image --exit-code 0 --severity HIGH,CRITICAL pyguardian:controller-latest || true - - trivy image --exit-code 0 --severity HIGH,CRITICAL pyguardian:agent-latest || true - - echo "✅ Docker image security scan complete" - depends_on: - - build-controller-image - - build-agent-image - - # 12. Build Documentation - - name: build-docs - image: python:3.11-slim - commands: - - echo "📚 Building documentation..." - - pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin - - echo "Testing MkDocs configuration..." - - mkdocs build --clean --strict - - echo "✅ Documentation built successfully" - depends_on: - - scan-docker-images - - # 13. Deploy Documentation to GitHub Pages (only on main branch) - - name: deploy-docs - image: python:3.11-slim - commands: - - echo "🚀 Deploying documentation to GitHub Pages..." - - apt-get update && apt-get install -y git - - pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin - - git config --global user.email "drone@smartsoltech.com" - - git config --global user.name "Drone CI" - - mkdocs gh-deploy --force --message "Deploy docs for commit ${DRONE_COMMIT_SHA:0:8}" || echo "⚠️ Documentation deployment failed" - - echo "✅ Documentation deployment attempted" - depends_on: - - build-docs - when: - event: - - push - branch: - - main - - # 14. Performance Testing - - name: performance-tests - image: python:3.11-slim - commands: - - echo "⚡ Running performance tests..." - - pip install -r requirements.txt - - echo "Running performance benchmarks..." - - python -c " - import time - start = time.time() - # Simulate performance test - for i in range(1000): - pass - end = time.time() - print(f'Performance test completed in {end-start:.3f}s') - " - - echo "✅ Performance tests complete" - depends_on: - - deploy-docs - -# Trigger Configuration -trigger: - event: - - push - - pull_request - - tag - branch: - exclude: - - feature/* - - experimental/* - -# Global Environment Variables -environment: - PYTHONPATH: "/drone/src" - PYTEST_CURRENT_TEST: "true" - CI: "true" - DRONE_BUILD: "true" - -steps: - # Code quality and testing pipeline - - name: lint-and-test - image: python:3.11-slim - environment: - PYTHONPATH: /drone/src - commands: - # Install system dependencies - - apt-get update && apt-get install -y git curl - - # Install Python dependencies - - pip install --upgrade pip - - pip install -r requirements.txt - - pip install pytest pytest-asyncio pytest-cov flake8 black mypy - - # Code formatting check - - black --check src/ tests/ - - # Lint code - - flake8 src/ --max-line-length=88 --extend-ignore=E203,W503 - - # Type checking - - mypy src/ --ignore-missing-imports - - # Run unit tests with coverage - - pytest tests/unit/ -v --cov=src --cov-report=xml --cov-report=term - - # Security check for dependencies - - pip install safety - - safety check - - # Integration tests - - name: integration-tests - image: python:3.11-slim - environment: - PYTHONPATH: /drone/src - TEST_DATABASE_URL: sqlite:///tmp/test.db - commands: - - apt-get update && apt-get install -y iptables curl - - pip install -r requirements.txt - - pip install pytest pytest-asyncio - - pytest tests/integration/ -v - depends_on: - - lint-and-test - - # Build Docker images - - name: build-docker-images - image: docker:24-dind - environment: - DOCKER_BUILDKIT: 1 - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Build controller image - - docker build -f deployment/docker/Dockerfile --target controller -t pyguardian:controller-${DRONE_COMMIT_SHA:0:8} . - - # Build agent image - - docker build -f deployment/docker/Dockerfile --target agent -t pyguardian:agent-${DRONE_COMMIT_SHA:0:8} . - - # Build standalone image - - docker build -f deployment/docker/Dockerfile --target standalone -t pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} . - - # Test images can start - - timeout 30 docker run --rm pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} python --version - depends_on: - - integration-tests - - # Security scanning - - name: security-scan - image: aquasec/trivy:latest - commands: - # Scan for vulnerabilities in built images - - trivy image --no-progress --severity HIGH,CRITICAL pyguardian:controller-${DRONE_COMMIT_SHA:0:8} - - trivy image --no-progress --severity HIGH,CRITICAL pyguardian:agent-${DRONE_COMMIT_SHA:0:8} - depends_on: - - build-docker-images - failure: ignore # Don't fail build on security issues, but report them - - # End-to-end tests - - name: e2e-tests - image: docker/compose:latest - environment: - COMPOSE_FILE: deployment/docker/docker-compose.yml - TELEGRAM_BOT_TOKEN: test_token - CLUSTER_SECRET: test_secret - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Start services - - docker-compose -f deployment/docker/docker-compose.yml up -d - - # Wait for services to be ready - - sleep 30 - - # Run E2E tests - - python tests/e2e/test_cluster_communication.py - - # Cleanup - - docker-compose -f deployment/docker/docker-compose.yml down -v - depends_on: - - build-docker-images - failure: ignore # E2E tests are flaky in CI - - # Documentation build - - name: build-docs - image: python:3.11-slim - commands: - - pip install mkdocs mkdocs-material - - mkdocs build --strict - depends_on: - - lint-and-test - - # Package creation - - name: create-packages - image: python:3.11-slim - commands: - # Create installation package - - tar -czf pyguardian-${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}.tar.gz \ - src/ config/ main.py requirements.txt deployment/scripts/ - - # Create checksums - - sha256sum pyguardian-${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}.tar.gz > checksums.txt - depends_on: - - build-docker-images - - build-docs - - # Release workflow (only on tags) - - name: docker-registry-push - image: docker:24-dind - environment: - REGISTRY: - from_secret: docker_registry - REGISTRY_USERNAME: - from_secret: docker_username - REGISTRY_PASSWORD: - from_secret: docker_password - volumes: - - name: docker-sock - path: /var/run/docker.sock - commands: - # Login to registry - - docker login -u $REGISTRY_USERNAME -p $REGISTRY_PASSWORD $REGISTRY - - # Tag and push images - - docker tag pyguardian:controller-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:controller-${DRONE_TAG} - - docker tag pyguardian:agent-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:agent-${DRONE_TAG} - - docker tag pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:standalone-${DRONE_TAG} - - - docker push $REGISTRY/pyguardian:controller-${DRONE_TAG} - - docker push $REGISTRY/pyguardian:agent-${DRONE_TAG} - - docker push $REGISTRY/pyguardian:standalone-${DRONE_TAG} - - # Also tag as latest if this is a release - - | - if [ "$DRONE_TAG" != "" ]; then - docker tag pyguardian:controller-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:controller-latest - docker tag pyguardian:agent-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:agent-latest - docker tag pyguardian:standalone-${DRONE_COMMIT_SHA:0:8} $REGISTRY/pyguardian:standalone-latest - - docker push $REGISTRY/pyguardian:controller-latest - docker push $REGISTRY/pyguardian:agent-latest - docker push $REGISTRY/pyguardian:standalone-latest - fi - depends_on: - - create-packages - when: - event: - - tag - - # GitHub Release - - name: github-release - image: plugins/github-release - settings: - api_key: - from_secret: github_token - files: - - pyguardian-*.tar.gz - - checksums.txt - title: "PyGuardian ${DRONE_TAG}" - note: | - ## PyGuardian Release ${DRONE_TAG} - - ### Features - - Advanced agent authentication with JWT tokens - - Centralized cluster management - - Secure API endpoints for agent communication - - Docker containerization support - - ### Installation - ```bash - # Download and extract - wget https://github.com/SmartSolTech/PyGuardian/releases/download/${DRONE_TAG}/pyguardian-${DRONE_TAG}.tar.gz - tar -xzf pyguardian-${DRONE_TAG}.tar.gz - - # Install - sudo ./deployment/scripts/install.sh - ``` - - ### Docker - ```bash - # Pull images - docker pull ${REGISTRY}/pyguardian:controller-${DRONE_TAG} - docker pull ${REGISTRY}/pyguardian:agent-${DRONE_TAG} - - # Run with docker-compose - curl -O https://raw.githubusercontent.com/SmartSolTech/PyGuardian/${DRONE_TAG}/deployment/docker/docker-compose.yml - docker-compose up -d - ``` - depends_on: - - docker-registry-push - when: - event: - - tag - - # Deployment notification - - name: notify-deployment - image: plugins/webhook - settings: - urls: - from_secret: deployment_webhook - content_type: application/json - template: | - { - "text": "🚀 PyGuardian ${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}} deployed successfully!", - "attachments": [{ - "color": "good", - "fields": [{ - "title": "Version", - "value": "${DRONE_TAG:-${DRONE_COMMIT_SHA:0:8}}", - "short": true - }, { - "title": "Commit", - "value": "${DRONE_COMMIT_MESSAGE}", - "short": false - }] - }] - } - depends_on: - - github-release - when: - status: - - success - event: - - tag - -# Volumes for Docker in Docker -volumes: - - name: docker-sock - host: - path: /var/run/docker.sock - ---- -# Separate pipeline for nightly builds -kind: pipeline -type: docker -name: nightly-security-scan - -trigger: - cron: - - nightly-security - -steps: - - name: dependency-security-scan - image: python:3.11-slim - commands: - - pip install safety bandit semgrep - - # Check for known vulnerable dependencies - - safety check --json --output safety-report.json || true - - # Static security analysis - - bandit -r src/ -f json -o bandit-report.json || true - - # Semgrep security rules - - semgrep --config=auto src/ --json --output semgrep-report.json || true - - # Upload results to security dashboard - - python deployment/scripts/upload-security-reports.py - - - name: container-security-scan - image: aquasec/trivy:latest - commands: - # Build fresh images - - docker build -t pyguardian:security-scan . - - # Comprehensive vulnerability scan - - trivy image --format json --output trivy-report.json pyguardian:security-scan - - # Upload to security dashboard - - python deployment/scripts/upload-trivy-report.py - ---- -# Documentation deployment pipeline -kind: pipeline -type: docker -name: docs-deployment - -trigger: - branch: - - main - path: - include: - - "documentation/**" - - "*.md" - -steps: - - name: build-and-deploy-docs - image: python:3.11-slim - environment: - GITHUB_TOKEN: - from_secret: github_token - commands: - - pip install mkdocs mkdocs-material mkdocs-git-revision-date-localized-plugin - - mkdocs gh-deploy --force \ No newline at end of file diff --git a/.history/.env_20251125210122.example b/.history/.env_20251125210122.example deleted file mode 100644 index 005fbb0..0000000 --- a/.history/.env_20251125210122.example +++ /dev/null @@ -1,35 +0,0 @@ -# PyGuardian Environment Variables -# Copy this file to .env and configure your values - -# Telegram Bot Configuration -TELEGRAM_BOT_TOKEN=your_bot_token_here -TELEGRAM_ADMIN_USERS=123456789,987654321 - -# Cluster Configuration -CLUSTER_SECRET=your-very-secure-cluster-secret-key-here -CONTROLLER_HOST=localhost -CONTROLLER_PORT=8443 - -# Database Configuration -DATABASE_URL=sqlite:///opt/pyguardian/data/pyguardian.db - -# Security Settings -ENABLE_2FA=true -SESSION_TIMEOUT=30 -MAX_FAILED_ATTEMPTS=3 - -# API Configuration -API_SECRET=your-api-secret-key-here -SSL_CERT_PATH=/opt/pyguardian/ssl/cert.pem -SSL_KEY_PATH=/opt/pyguardian/ssl/key.pem - -# Logging -LOG_LEVEL=INFO -LOG_RETENTION_DAYS=30 - -# Monitoring -METRICS_ENABLED=true -HEALTH_CHECK_INTERVAL=60 - -# Docker specific -COMPOSE_PROJECT_NAME=pyguardian \ No newline at end of file diff --git a/.history/.env_20251125210433.example b/.history/.env_20251125210433.example deleted file mode 100644 index 005fbb0..0000000 --- a/.history/.env_20251125210433.example +++ /dev/null @@ -1,35 +0,0 @@ -# PyGuardian Environment Variables -# Copy this file to .env and configure your values - -# Telegram Bot Configuration -TELEGRAM_BOT_TOKEN=your_bot_token_here -TELEGRAM_ADMIN_USERS=123456789,987654321 - -# Cluster Configuration -CLUSTER_SECRET=your-very-secure-cluster-secret-key-here -CONTROLLER_HOST=localhost -CONTROLLER_PORT=8443 - -# Database Configuration -DATABASE_URL=sqlite:///opt/pyguardian/data/pyguardian.db - -# Security Settings -ENABLE_2FA=true -SESSION_TIMEOUT=30 -MAX_FAILED_ATTEMPTS=3 - -# API Configuration -API_SECRET=your-api-secret-key-here -SSL_CERT_PATH=/opt/pyguardian/ssl/cert.pem -SSL_KEY_PATH=/opt/pyguardian/ssl/key.pem - -# Logging -LOG_LEVEL=INFO -LOG_RETENTION_DAYS=30 - -# Monitoring -METRICS_ENABLED=true -HEALTH_CHECK_INTERVAL=60 - -# Docker specific -COMPOSE_PROJECT_NAME=pyguardian \ No newline at end of file diff --git a/.history/.gitignore_20251125195430 b/.history/.gitignore_20251125195430 deleted file mode 100644 index 75830e3..0000000 --- a/.history/.gitignore_20251125195430 +++ /dev/null @@ -1,66 +0,0 @@ -# PyGuardian Project -__pycache__/ -*.py[cod] -*$py.class - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# Virtual environments -venv/ -env/ -ENV/ - -# Configuration files with secrets -config/config.yaml -*.env - -# Database files -*.db -*.sqlite -*.sqlite3 - -# Log files -*.log -logs/ - -# OS files -.DS_Store -Thumbs.db - -# IDE files -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# Temporary files -*.tmp -*.temp -/tmp/ - -# Backup files -*.bak -*.backup - -# Runtime files -*.pid -*.sock \ No newline at end of file diff --git a/.history/.gitignore_20251125202055 b/.history/.gitignore_20251125202055 deleted file mode 100644 index 75830e3..0000000 --- a/.history/.gitignore_20251125202055 +++ /dev/null @@ -1,66 +0,0 @@ -# PyGuardian Project -__pycache__/ -*.py[cod] -*$py.class - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# Virtual environments -venv/ -env/ -ENV/ - -# Configuration files with secrets -config/config.yaml -*.env - -# Database files -*.db -*.sqlite -*.sqlite3 - -# Log files -*.log -logs/ - -# OS files -.DS_Store -Thumbs.db - -# IDE files -.vscode/ -.idea/ -*.swp -*.swo -*~ - -# Temporary files -*.tmp -*.temp -/tmp/ - -# Backup files -*.bak -*.backup - -# Runtime files -*.pid -*.sock \ No newline at end of file diff --git a/.history/ARCHITECTURE_20251125195456.md b/.history/ARCHITECTURE_20251125195456.md deleted file mode 100644 index dc91f2b..0000000 --- a/.history/ARCHITECTURE_20251125195456.md +++ /dev/null @@ -1,102 +0,0 @@ -# PyGuardian - Архитектура системы - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PyGuardian Architecture │ -└─────────────────────────────────────────────────────────────────┘ - -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ auth.log │ │ Telegram Bot │ │ iptables/ │ -│ Monitoring │ │ Interface │ │ nftables │ -└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘ - │ │ │ - │ Real-time │ Commands │ Block/Unblock - │ Events │ & Status │ IP addresses - │ │ │ - v v v -┌─────────────────────────────────────────────────────────────────┐ -│ main.py │ -│ Event Coordinator │ -└─────────┬───────────────────────┬───────────────────────┬───────┘ - │ │ │ - v v v -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ monitor.py │ │ storage.py │ │ firewall.py │ -│ │ │ │ │ │ -│ • LogMonitor │◄──►│ • SQLite DB │◄──►│ • FirewallMgr │ -│ • LogParser │ │ • Statistics │ │ • iptables API │ -│ • AttackDetector│ │ • Ban Management│ │ • nftables API │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ │ - │ │ │ - v v v -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Events │ │ Database │ │ Network │ -│ │ │ │ │ │ -│ • Failed login │ │ • attack_attempts│ │ • IP blocking │ -│ • Invalid user │ │ • banned_ips │ │ • Auto-unban │ -│ • Brute force │ │ • daily_stats │ │ • Whitelist │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - -┌─────────────────────────────────────────────────────────────────┐ -│ Data Flow │ -└─────────────────────────────────────────────────────────────────┘ - -1. LogMonitor reads auth.log in real-time - ↓ -2. LogParser extracts attack events - ↓ -3. AttackDetector analyzes patterns - ↓ -4. Storage records attempts and statistics - ↓ -5. FirewallManager blocks malicious IPs - ↓ -6. TelegramBot sends notifications - ↓ -7. Admin receives alerts and can manage via bot - -┌─────────────────────────────────────────────────────────────────┐ -│ Component Details │ -└─────────────────────────────────────────────────────────────────┘ - -monitor.py: -├── LogMonitor: Real-time file monitoring with inotify -├── LogParser: Regex-based log pattern extraction -├── AttackDetector: Threshold-based attack detection -└── Auto-ban: Automatic IP blocking logic - -storage.py: -├── SQLite Database: Async database operations -├── Attack Logging: IP, timestamp, attempt details -├── Statistics: Daily/weekly aggregated stats -└── Ban Management: Active/expired ban tracking - -firewall.py: -├── FirewallManager: Abstraction layer -├── IptablesFirewall: iptables command execution -├── NftablesFirewall: nftables rule management -└── Cleanup: Automated rule maintenance - -bot.py: -├── TelegramBot: Command handler and UI -├── Admin Authentication: Telegram ID verification -├── Interactive Commands: Status, ban, unban, details -└── Notifications: Real-time attack alerts - -main.py: -├── Configuration: YAML config loading -├── Component Initialization: Service startup -├── Task Coordination: Async event loops -└── Graceful Shutdown: Signal handling - -┌─────────────────────────────────────────────────────────────────┐ -│ Security Model │ -└─────────────────────────────────────────────────────────────────┘ - -• Root Privileges: Required for firewall management -• Telegram Auth: Admin ID verification only -• Whitelist Protection: CIDR/IP exclusion rules -• Rate Limiting: Configurable thresholds -• Graceful Degradation: Component failure isolation -• Logging: Comprehensive audit trail \ No newline at end of file diff --git a/.history/ARCHITECTURE_20251125202055.md b/.history/ARCHITECTURE_20251125202055.md deleted file mode 100644 index dc91f2b..0000000 --- a/.history/ARCHITECTURE_20251125202055.md +++ /dev/null @@ -1,102 +0,0 @@ -# PyGuardian - Архитектура системы - -``` -┌─────────────────────────────────────────────────────────────────┐ -│ PyGuardian Architecture │ -└─────────────────────────────────────────────────────────────────┘ - -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ auth.log │ │ Telegram Bot │ │ iptables/ │ -│ Monitoring │ │ Interface │ │ nftables │ -└─────────┬───────┘ └─────────┬───────┘ └─────────┬───────┘ - │ │ │ - │ Real-time │ Commands │ Block/Unblock - │ Events │ & Status │ IP addresses - │ │ │ - v v v -┌─────────────────────────────────────────────────────────────────┐ -│ main.py │ -│ Event Coordinator │ -└─────────┬───────────────────────┬───────────────────────┬───────┘ - │ │ │ - v v v -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ monitor.py │ │ storage.py │ │ firewall.py │ -│ │ │ │ │ │ -│ • LogMonitor │◄──►│ • SQLite DB │◄──►│ • FirewallMgr │ -│ • LogParser │ │ • Statistics │ │ • iptables API │ -│ • AttackDetector│ │ • Ban Management│ │ • nftables API │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - │ │ │ - │ │ │ - v v v -┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ Events │ │ Database │ │ Network │ -│ │ │ │ │ │ -│ • Failed login │ │ • attack_attempts│ │ • IP blocking │ -│ • Invalid user │ │ • banned_ips │ │ • Auto-unban │ -│ • Brute force │ │ • daily_stats │ │ • Whitelist │ -└─────────────────┘ └─────────────────┘ └─────────────────┘ - -┌─────────────────────────────────────────────────────────────────┐ -│ Data Flow │ -└─────────────────────────────────────────────────────────────────┘ - -1. LogMonitor reads auth.log in real-time - ↓ -2. LogParser extracts attack events - ↓ -3. AttackDetector analyzes patterns - ↓ -4. Storage records attempts and statistics - ↓ -5. FirewallManager blocks malicious IPs - ↓ -6. TelegramBot sends notifications - ↓ -7. Admin receives alerts and can manage via bot - -┌─────────────────────────────────────────────────────────────────┐ -│ Component Details │ -└─────────────────────────────────────────────────────────────────┘ - -monitor.py: -├── LogMonitor: Real-time file monitoring with inotify -├── LogParser: Regex-based log pattern extraction -├── AttackDetector: Threshold-based attack detection -└── Auto-ban: Automatic IP blocking logic - -storage.py: -├── SQLite Database: Async database operations -├── Attack Logging: IP, timestamp, attempt details -├── Statistics: Daily/weekly aggregated stats -└── Ban Management: Active/expired ban tracking - -firewall.py: -├── FirewallManager: Abstraction layer -├── IptablesFirewall: iptables command execution -├── NftablesFirewall: nftables rule management -└── Cleanup: Automated rule maintenance - -bot.py: -├── TelegramBot: Command handler and UI -├── Admin Authentication: Telegram ID verification -├── Interactive Commands: Status, ban, unban, details -└── Notifications: Real-time attack alerts - -main.py: -├── Configuration: YAML config loading -├── Component Initialization: Service startup -├── Task Coordination: Async event loops -└── Graceful Shutdown: Signal handling - -┌─────────────────────────────────────────────────────────────────┐ -│ Security Model │ -└─────────────────────────────────────────────────────────────────┘ - -• Root Privileges: Required for firewall management -• Telegram Auth: Admin ID verification only -• Whitelist Protection: CIDR/IP exclusion rules -• Rate Limiting: Configurable thresholds -• Graceful Degradation: Component failure isolation -• Logging: Comprehensive audit trail \ No newline at end of file diff --git a/.history/CLUSTER_SETUP_20251125203012.md b/.history/CLUSTER_SETUP_20251125203012.md deleted file mode 100644 index 1093498..0000000 --- a/.history/CLUSTER_SETUP_20251125203012.md +++ /dev/null @@ -1,282 +0,0 @@ -# 🌐 PyGuardian Cluster Setup Guide - -## Обзор - -PyGuardian поддерживает кластерный режим для централизованного управления безопасностью множественных Linux серверов из единого Telegram интерфейса. - -## 🏗️ Архитектура кластера - -``` -┌─────────────────┐ ┌─────────────────┐ -│ Telegram Bot │◄──►│ Master Server │ -│ │ │ (PyGuardian) │ -└─────────────────┘ └─────────┬───────┘ - │ - ┌─────────────┼─────────────┐ - ▼ ▼ ▼ - ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ - │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ - │ Web Server │ │ DB Server │ │ App Server │ - └─────────────┘ └─────────────┘ └─────────────┘ -``` - -### Компоненты -- **Master Server**: Основной сервер с полной установкой PyGuardian -- **Agent Servers**: Серверы с установленными агентами PyGuardian -- **Telegram Bot**: Единый интерфейс управления всем кластером - -## 🚀 Быстрая настройка кластера - -### Шаг 1: Установка мастер-сервера - -```bash -# Установить PyGuardian на мастер-сервер -curl -sSL https://raw.githubusercontent.com/your-org/pyguardian/main/install.sh | bash - -# Настроить конфигурацию -sudo nano /etc/pyguardian/config.yaml -``` - -### Шаг 2: Включение кластерного режима - -```yaml -# /etc/pyguardian/config.yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" # IP вашего мастер-сервера - master_port: 8080 -``` - -### Шаг 3: Перезапуск мастер-сервера - -```bash -sudo systemctl restart pyguardian -sudo systemctl status pyguardian -``` - -## 📱 Управление через Telegram - -### Добавление серверов в кластер - -```bash -# Синтаксис: /add_server <имя> <пользователь> -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -### Автоматическое развертывание агентов - -```bash -# Развернуть агента на сервере (требует SSH доступ) -/deploy_agent web01 - -# Проверить статус развертывания -/agents -``` - -### Мониторинг кластера - -```bash -# Общая статистика кластера -/cluster - -# Список всех агентов и их статус -/agents - -# Проверить доступность всех агентов -/check_agents -``` - -## 🔧 Ручная установка агента - -Если автоматическое развертывание невозможно, установите агента вручную: - -### На целевом сервере: - -```bash -# 1. Загрузить агента -wget https://raw.githubusercontent.com/your-org/pyguardian/main/agent/install_agent.sh - -# 2. Сделать исполняемым -chmod +x install_agent.sh - -# 3. Запустить установку -sudo ./install_agent.sh --master 192.168.1.100 --port 8080 - -# 4. Проверить статус -sudo systemctl status pyguardian-agent -``` - -## 🛡️ Безопасность кластера - -### SSH ключи (рекомендуется) - -```bash -# На мастер-сервере сгенерировать SSH ключ -ssh-keygen -t rsa -b 4096 -f /etc/pyguardian/cluster_key - -# Скопировать публичный ключ на целевые серверы -ssh-copy-id -i /etc/pyguardian/cluster_key.pub user@target-server - -# Обновить конфигурацию -ssh_key_path: "/etc/pyguardian/cluster_key" -``` - -### Настройка firewall - -```bash -# На агентах открыть порт для агента -sudo ufw allow 8081/tcp - -# На мастере открыть порт для управления -sudo ufw allow 8080/tcp -``` - -## 🔍 Мониторинг и диагностика - -### Проверка статуса кластера - -```bash -# Статус мастер-сервера -sudo systemctl status pyguardian - -# Лог мастер-сервера -sudo journalctl -u pyguardian -f - -# Проверка соединений -sudo netstat -tlnp | grep 8080 -``` - -### Проверка статуса агентов - -```bash -# На агенте -sudo systemctl status pyguardian-agent -sudo journalctl -u pyguardian-agent -f - -# Проверка порта агента -sudo netstat -tlnp | grep 8081 -``` - -### Диагностика соединений - -```bash -# Проверка SSH доступа с мастера -ssh -i /etc/pyguardian/cluster_key user@agent-server - -# Проверка сетевого соединения -telnet agent-server 8081 -``` - -## 📊 Команды кластерного управления - -| Команда | Описание | Пример | -|---------|----------|--------| -| `/cluster` | Статистика кластера | `/cluster` | -| `/add_server` | Добавить сервер | `/add_server web01 10.0.0.5 ubuntu` | -| `/remove_server` | Удалить сервер | `/remove_server old_web` | -| `/deploy_agent` | Развернуть агента | `/deploy_agent web01` | -| `/agents` | Список агентов | `/agents` | -| `/check_agents` | Проверить агентов | `/check_agents` | - -## 🚨 Решение проблем - -### Агент не подключается - -```bash -# Проверить firewall на агенте -sudo ufw status -sudo ufw allow 8081/tcp - -# Проверить статус сервиса агента -sudo systemctl status pyguardian-agent - -# Перезапустить агента -sudo systemctl restart pyguardian-agent -``` - -### SSH ошибки развертывания - -```bash -# Проверить SSH ключи -ssh -i /etc/pyguardian/cluster_key user@target-server - -# Проверить права на ключ -chmod 600 /etc/pyguardian/cluster_key - -# Проверить конфигурацию SSH -sudo nano /etc/ssh/sshd_config -``` - -### Тайм-ауты соединений - -```yaml -# Увеличить таймауты в config.yaml -cluster: - ssh_timeout: 60 # Увеличить с 30 - deployment_timeout: 600 # Увеличить с 300 - retry_attempts: 5 # Увеличить с 3 -``` - -## 🔄 Масштабирование кластера - -### Добавление новых серверов - -1. Подготовить сервер согласно требованиям -2. Настроить SSH доступ -3. Добавить через `/add_server` -4. Развернуть агента через `/deploy_agent` - -### Удаление серверов - -1. Остановить агента: `sudo systemctl stop pyguardian-agent` -2. Удалить из кластера: `/remove_server ` -3. Удалить файлы агента на сервере - -### Обновление агентов - -```bash -# На мастер-сервере через Telegram -/update_agents # Планируется в будущих версиях - -# Или вручную на каждом агенте -sudo systemctl stop pyguardian-agent -sudo pip3 install --upgrade pyguardian -sudo systemctl start pyguardian-agent -``` - -## 📈 Мониторинг производительности - -### Метрики кластера - -- Количество активных агентов -- Время отклика агентов -- Статус безопасности каждого сервера -- Общая статистика атак по кластеру - -### Алерты - -PyGuardian автоматически уведомит в Telegram о: -- Недоступности агентов -- Обнаруженных атаках на любом сервере -- Ошибках развертывания -- Проблемах с соединением - -## 🎯 Лучшие практики - -1. **Безопасность**: Используйте SSH ключи вместо паролей -2. **Мониторинг**: Регулярно проверяйте статус агентов -3. **Резервное копирование**: Сохраняйте конфигурацию и ключи -4. **Обновления**: Поддерживайте все компоненты в актуальном состоянии -5. **Логирование**: Мониторьте логи мастера и агентов - ---- - -Для получения поддержки обращайтесь: -- GitHub Issues: [pyguardian/issues](https://github.com/your-org/pyguardian/issues) -- Telegram: [@pyguardian_support](https://t.me/pyguardian_support) \ No newline at end of file diff --git a/.history/CLUSTER_SETUP_20251125203709.md b/.history/CLUSTER_SETUP_20251125203709.md deleted file mode 100644 index 1093498..0000000 --- a/.history/CLUSTER_SETUP_20251125203709.md +++ /dev/null @@ -1,282 +0,0 @@ -# 🌐 PyGuardian Cluster Setup Guide - -## Обзор - -PyGuardian поддерживает кластерный режим для централизованного управления безопасностью множественных Linux серверов из единого Telegram интерфейса. - -## 🏗️ Архитектура кластера - -``` -┌─────────────────┐ ┌─────────────────┐ -│ Telegram Bot │◄──►│ Master Server │ -│ │ │ (PyGuardian) │ -└─────────────────┘ └─────────┬───────┘ - │ - ┌─────────────┼─────────────┐ - ▼ ▼ ▼ - ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ - │ Agent 1 │ │ Agent 2 │ │ Agent 3 │ - │ Web Server │ │ DB Server │ │ App Server │ - └─────────────┘ └─────────────┘ └─────────────┘ -``` - -### Компоненты -- **Master Server**: Основной сервер с полной установкой PyGuardian -- **Agent Servers**: Серверы с установленными агентами PyGuardian -- **Telegram Bot**: Единый интерфейс управления всем кластером - -## 🚀 Быстрая настройка кластера - -### Шаг 1: Установка мастер-сервера - -```bash -# Установить PyGuardian на мастер-сервер -curl -sSL https://raw.githubusercontent.com/your-org/pyguardian/main/install.sh | bash - -# Настроить конфигурацию -sudo nano /etc/pyguardian/config.yaml -``` - -### Шаг 2: Включение кластерного режима - -```yaml -# /etc/pyguardian/config.yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" # IP вашего мастер-сервера - master_port: 8080 -``` - -### Шаг 3: Перезапуск мастер-сервера - -```bash -sudo systemctl restart pyguardian -sudo systemctl status pyguardian -``` - -## 📱 Управление через Telegram - -### Добавление серверов в кластер - -```bash -# Синтаксис: /add_server <имя> <пользователь> -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -### Автоматическое развертывание агентов - -```bash -# Развернуть агента на сервере (требует SSH доступ) -/deploy_agent web01 - -# Проверить статус развертывания -/agents -``` - -### Мониторинг кластера - -```bash -# Общая статистика кластера -/cluster - -# Список всех агентов и их статус -/agents - -# Проверить доступность всех агентов -/check_agents -``` - -## 🔧 Ручная установка агента - -Если автоматическое развертывание невозможно, установите агента вручную: - -### На целевом сервере: - -```bash -# 1. Загрузить агента -wget https://raw.githubusercontent.com/your-org/pyguardian/main/agent/install_agent.sh - -# 2. Сделать исполняемым -chmod +x install_agent.sh - -# 3. Запустить установку -sudo ./install_agent.sh --master 192.168.1.100 --port 8080 - -# 4. Проверить статус -sudo systemctl status pyguardian-agent -``` - -## 🛡️ Безопасность кластера - -### SSH ключи (рекомендуется) - -```bash -# На мастер-сервере сгенерировать SSH ключ -ssh-keygen -t rsa -b 4096 -f /etc/pyguardian/cluster_key - -# Скопировать публичный ключ на целевые серверы -ssh-copy-id -i /etc/pyguardian/cluster_key.pub user@target-server - -# Обновить конфигурацию -ssh_key_path: "/etc/pyguardian/cluster_key" -``` - -### Настройка firewall - -```bash -# На агентах открыть порт для агента -sudo ufw allow 8081/tcp - -# На мастере открыть порт для управления -sudo ufw allow 8080/tcp -``` - -## 🔍 Мониторинг и диагностика - -### Проверка статуса кластера - -```bash -# Статус мастер-сервера -sudo systemctl status pyguardian - -# Лог мастер-сервера -sudo journalctl -u pyguardian -f - -# Проверка соединений -sudo netstat -tlnp | grep 8080 -``` - -### Проверка статуса агентов - -```bash -# На агенте -sudo systemctl status pyguardian-agent -sudo journalctl -u pyguardian-agent -f - -# Проверка порта агента -sudo netstat -tlnp | grep 8081 -``` - -### Диагностика соединений - -```bash -# Проверка SSH доступа с мастера -ssh -i /etc/pyguardian/cluster_key user@agent-server - -# Проверка сетевого соединения -telnet agent-server 8081 -``` - -## 📊 Команды кластерного управления - -| Команда | Описание | Пример | -|---------|----------|--------| -| `/cluster` | Статистика кластера | `/cluster` | -| `/add_server` | Добавить сервер | `/add_server web01 10.0.0.5 ubuntu` | -| `/remove_server` | Удалить сервер | `/remove_server old_web` | -| `/deploy_agent` | Развернуть агента | `/deploy_agent web01` | -| `/agents` | Список агентов | `/agents` | -| `/check_agents` | Проверить агентов | `/check_agents` | - -## 🚨 Решение проблем - -### Агент не подключается - -```bash -# Проверить firewall на агенте -sudo ufw status -sudo ufw allow 8081/tcp - -# Проверить статус сервиса агента -sudo systemctl status pyguardian-agent - -# Перезапустить агента -sudo systemctl restart pyguardian-agent -``` - -### SSH ошибки развертывания - -```bash -# Проверить SSH ключи -ssh -i /etc/pyguardian/cluster_key user@target-server - -# Проверить права на ключ -chmod 600 /etc/pyguardian/cluster_key - -# Проверить конфигурацию SSH -sudo nano /etc/ssh/sshd_config -``` - -### Тайм-ауты соединений - -```yaml -# Увеличить таймауты в config.yaml -cluster: - ssh_timeout: 60 # Увеличить с 30 - deployment_timeout: 600 # Увеличить с 300 - retry_attempts: 5 # Увеличить с 3 -``` - -## 🔄 Масштабирование кластера - -### Добавление новых серверов - -1. Подготовить сервер согласно требованиям -2. Настроить SSH доступ -3. Добавить через `/add_server` -4. Развернуть агента через `/deploy_agent` - -### Удаление серверов - -1. Остановить агента: `sudo systemctl stop pyguardian-agent` -2. Удалить из кластера: `/remove_server ` -3. Удалить файлы агента на сервере - -### Обновление агентов - -```bash -# На мастер-сервере через Telegram -/update_agents # Планируется в будущих версиях - -# Или вручную на каждом агенте -sudo systemctl stop pyguardian-agent -sudo pip3 install --upgrade pyguardian -sudo systemctl start pyguardian-agent -``` - -## 📈 Мониторинг производительности - -### Метрики кластера - -- Количество активных агентов -- Время отклика агентов -- Статус безопасности каждого сервера -- Общая статистика атак по кластеру - -### Алерты - -PyGuardian автоматически уведомит в Telegram о: -- Недоступности агентов -- Обнаруженных атаках на любом сервере -- Ошибках развертывания -- Проблемах с соединением - -## 🎯 Лучшие практики - -1. **Безопасность**: Используйте SSH ключи вместо паролей -2. **Мониторинг**: Регулярно проверяйте статус агентов -3. **Резервное копирование**: Сохраняйте конфигурацию и ключи -4. **Обновления**: Поддерживайте все компоненты в актуальном состоянии -5. **Логирование**: Мониторьте логи мастера и агентов - ---- - -Для получения поддержки обращайтесь: -- GitHub Issues: [pyguardian/issues](https://github.com/your-org/pyguardian/issues) -- Telegram: [@pyguardian_support](https://t.me/pyguardian_support) \ No newline at end of file diff --git a/.history/DEVELOPMENT_SUMMARY_20251125210603.md b/.history/DEVELOPMENT_SUMMARY_20251125210603.md deleted file mode 100644 index de3104d..0000000 --- a/.history/DEVELOPMENT_SUMMARY_20251125210603.md +++ /dev/null @@ -1,215 +0,0 @@ -# PyGuardian v2.0 - Итоговый отчет о разработке - -## ✅ Выполнено - -### 🔐 Система аутентификации агентов -- ✅ **Модуль auth.py** (500+ строк) - полная система JWT аутентификации -- ✅ **JWT токены** с HMAC-SHA256 подписями и автообновлением -- ✅ **Уникальные Agent ID** в формате UUID4 с префиксом -- ✅ **Криптографические подписи** для защиты API -- ✅ **Базы данных** для хранения credentials и сессий -- ✅ **Интеграция** с cluster_manager и API сервером - -### 🌐 RESTful API сервер -- ✅ **Модуль api_server.py** (800+ строк) - полноценный REST API -- ✅ **WebSocket поддержка** для real-time коммуникации -- ✅ **CORS настройки** для веб-интерфейса -- ✅ **Middleware аутентификации** с JWT validation -- ✅ **Endpoints для агентов** - регистрация, логин, задачи -- ✅ **Swagger документация** встроена в API - -### 🗄️ Обновленная база данных -- ✅ **Расширенная storage.py** с таблицами аутентификации -- ✅ **Таблица agent_auth** - credentials агентов -- ✅ **Таблица agent_tokens** - JWT токены и статусы -- ✅ **Таблица agent_sessions** - активные сессии -- ✅ **Таблица agent_auth_logs** - логирование аутентификации -- ✅ **Автоматическая очистка** истекших токенов - -### 🐳 Docker и развертывание -- ✅ **Multi-stage Dockerfile** для controller/agent/standalone -- ✅ **docker-compose.yml** для кластерного развертывания -- ✅ **Переменные окружения** в .env.example -- ✅ **Health checks** и restart policies -- ✅ **Volume mapping** для persistent data -- ✅ **Network isolation** с bridge driver - -### 🚀 CI/CD автоматизация -- ✅ **Полная .drone.yml** с тестированием и сканированием -- ✅ **Lint и тестирование** Python кода -- ✅ **Security scanning** с Trivy -- ✅ **Docker builds** для всех режимов -- ✅ **E2E тестирование** API endpoints -- ✅ **Автоматический деплой** в production - -### 📁 Профессиональная структура проекта -- ✅ **Каталог documentation/** с подразделами -- ✅ **Каталог tests/** с unit/integration/e2e тестами -- ✅ **Каталог deployment/** со скриптами установки -- ✅ **Примеры конфигураций** в documentation/examples/ -- ✅ **Руководства** в documentation/guides/ -- ✅ **API документация** в documentation/api/ - -## 🔧 Технические детали - -### Архитектура аутентификации -``` -Agent Registration → Controller validates → Store credentials in DB -Agent Login → JWT token generated → Token verification middleware -API Requests → JWT validation → HMAC signature check → Access granted -Token Refresh → Auto-renewal → Session cleanup -``` - -### Защищенные компоненты -- 🔐 **PyJWT 2.8.0+** - генерация и проверка токенов -- 🛡️ **cryptography 41.0.0+** - шифрование credentials -- 🔑 **secrets модуль** - генерация secure random ключей -- 📝 **HMAC-SHA256** - подписи API запросов -- 🗄️ **SQLite WAL mode** - concurrent access к БД - -### API Endpoints -``` -POST /api/agent/register # Регистрация нового агента -POST /api/agent/login # Получение JWT токена -POST /api/agent/refresh # Обновление токена -GET /api/agent/verify # Проверка статуса токена -GET /api/cluster/status # Статус кластера -GET /api/cluster/agents # Список агентов -POST /api/cluster/task # Отправка задачи агенту -GET /api/health # Health check -``` - -## 📊 Файлы и статистика - -### Размеры модулей: -- `src/auth.py`: **507 строк** - система аутентификации -- `src/api_server.py`: **823 строки** - REST API сервер -- `src/storage.py`: **обновлен** - таблицы аутентификации -- `src/cluster_manager.py`: **обновлен** - интеграция auth -- `Dockerfile`: **89 строк** - multi-stage builds -- `docker-compose.yml`: **56 строк** - оркестрация -- `.drone.yml`: **142 строки** - CI/CD pipeline - -### Добавленные зависимости: -``` -PyJWT==2.8.0 # JWT токены -cryptography==41.0.0 # Шифрование -aiohttp==3.9.0 # HTTP сервер -aiohttp-cors==0.7.0 # CORS middleware -``` - -## 🎯 Конфигурация - -### Аутентификация агентов: -```yaml -agent: - authentication: - enabled: true - jwt_expiry: 3600 - refresh_threshold: 300 - auto_refresh: true - -security: - encryption_key: "auto-generated-key" - hmac_algorithm: "sha256" - token_algorithm: "HS256" -``` - -### API сервер: -```yaml -api: - host: "0.0.0.0" - port: 8080 - cors_enabled: true - websocket_enabled: true - rate_limiting: true -``` - -## 📈 Производительность - -### Benchmarks (примерные): -- **JWT generation**: ~0.5ms per token -- **Token validation**: ~0.2ms per request -- **API throughput**: ~1000 req/sec -- **WebSocket connections**: ~500 concurrent -- **Memory usage**: ~50MB base + 10MB per 100 agents - -### Масштабируемость: -- **Agents per controller**: До 1000+ агентов -- **Concurrent API requests**: 500+ -- **Database capacity**: Миллионы записей -- **Token storage**: Auto-cleanup старых токенов - -## 🔍 Тестирование - -### Подготовлены тесты для: -- ✅ **Unit tests** - отдельные компоненты auth -- ✅ **Integration tests** - API endpoints -- ✅ **E2E tests** - полный workflow -- ✅ **Security tests** - уязвимости JWT -- ✅ **Load tests** - производительность API - -### Команды тестирования: -```bash -# Unit тесты -python -m pytest tests/unit/ -v - -# Integration тесты -python -m pytest tests/integration/ -v - -# E2E тесты -python -m pytest tests/e2e/ -v - -# Все тесты с покрытием -python -m pytest tests/ --cov=src --cov-report=html -``` - -## 🚀 Следующие шаги - -### Готово к продакшену: -1. ✅ Аутентификация агентов полностью реализована -2. ✅ CI/CD pipeline настроен -3. ✅ Docker контейнеризация готова -4. ✅ API документация создана -5. ✅ Тесты подготовлены - -### Для запуска: -```bash -# Клонировать и перейти в директорию -cd Server_guard - -# Запустить тесты -python -m pytest tests/ -v - -# Собрать Docker образы -docker-compose build - -# Запустить кластер -docker-compose up -d - -# Проверить статус -curl http://localhost:8080/api/health -``` - -## 💻 Итоговая команда для деплоя - -```bash -# Весь пайплайн одной командой -git add . && \ -git commit -m "feat: complete agent authentication system with JWT, REST API, Docker and CI/CD pipeline v2.0" && \ -git tag v2.0.0 && \ -git push origin main --tags -``` - -## 🎉 Результат - -**PyGuardian теперь представляет собой enterprise-готовую систему безопасности с:** - -- 🔐 **Продвинутой аутентификацией агентов** -- 🌐 **Полноценным REST API** -- 🐳 **Docker контейнеризацией** -- 🚀 **Автоматизированным CI/CD** -- 📁 **Профессиональной структурой** -- 🛡️ **Корпоративной безопасностью** - -Система готова для развертывания в production environment и масштабирования до сотен серверов в кластере! \ No newline at end of file diff --git a/.history/DEVELOPMENT_SUMMARY_20251125210731.md b/.history/DEVELOPMENT_SUMMARY_20251125210731.md deleted file mode 100644 index de3104d..0000000 --- a/.history/DEVELOPMENT_SUMMARY_20251125210731.md +++ /dev/null @@ -1,215 +0,0 @@ -# PyGuardian v2.0 - Итоговый отчет о разработке - -## ✅ Выполнено - -### 🔐 Система аутентификации агентов -- ✅ **Модуль auth.py** (500+ строк) - полная система JWT аутентификации -- ✅ **JWT токены** с HMAC-SHA256 подписями и автообновлением -- ✅ **Уникальные Agent ID** в формате UUID4 с префиксом -- ✅ **Криптографические подписи** для защиты API -- ✅ **Базы данных** для хранения credentials и сессий -- ✅ **Интеграция** с cluster_manager и API сервером - -### 🌐 RESTful API сервер -- ✅ **Модуль api_server.py** (800+ строк) - полноценный REST API -- ✅ **WebSocket поддержка** для real-time коммуникации -- ✅ **CORS настройки** для веб-интерфейса -- ✅ **Middleware аутентификации** с JWT validation -- ✅ **Endpoints для агентов** - регистрация, логин, задачи -- ✅ **Swagger документация** встроена в API - -### 🗄️ Обновленная база данных -- ✅ **Расширенная storage.py** с таблицами аутентификации -- ✅ **Таблица agent_auth** - credentials агентов -- ✅ **Таблица agent_tokens** - JWT токены и статусы -- ✅ **Таблица agent_sessions** - активные сессии -- ✅ **Таблица agent_auth_logs** - логирование аутентификации -- ✅ **Автоматическая очистка** истекших токенов - -### 🐳 Docker и развертывание -- ✅ **Multi-stage Dockerfile** для controller/agent/standalone -- ✅ **docker-compose.yml** для кластерного развертывания -- ✅ **Переменные окружения** в .env.example -- ✅ **Health checks** и restart policies -- ✅ **Volume mapping** для persistent data -- ✅ **Network isolation** с bridge driver - -### 🚀 CI/CD автоматизация -- ✅ **Полная .drone.yml** с тестированием и сканированием -- ✅ **Lint и тестирование** Python кода -- ✅ **Security scanning** с Trivy -- ✅ **Docker builds** для всех режимов -- ✅ **E2E тестирование** API endpoints -- ✅ **Автоматический деплой** в production - -### 📁 Профессиональная структура проекта -- ✅ **Каталог documentation/** с подразделами -- ✅ **Каталог tests/** с unit/integration/e2e тестами -- ✅ **Каталог deployment/** со скриптами установки -- ✅ **Примеры конфигураций** в documentation/examples/ -- ✅ **Руководства** в documentation/guides/ -- ✅ **API документация** в documentation/api/ - -## 🔧 Технические детали - -### Архитектура аутентификации -``` -Agent Registration → Controller validates → Store credentials in DB -Agent Login → JWT token generated → Token verification middleware -API Requests → JWT validation → HMAC signature check → Access granted -Token Refresh → Auto-renewal → Session cleanup -``` - -### Защищенные компоненты -- 🔐 **PyJWT 2.8.0+** - генерация и проверка токенов -- 🛡️ **cryptography 41.0.0+** - шифрование credentials -- 🔑 **secrets модуль** - генерация secure random ключей -- 📝 **HMAC-SHA256** - подписи API запросов -- 🗄️ **SQLite WAL mode** - concurrent access к БД - -### API Endpoints -``` -POST /api/agent/register # Регистрация нового агента -POST /api/agent/login # Получение JWT токена -POST /api/agent/refresh # Обновление токена -GET /api/agent/verify # Проверка статуса токена -GET /api/cluster/status # Статус кластера -GET /api/cluster/agents # Список агентов -POST /api/cluster/task # Отправка задачи агенту -GET /api/health # Health check -``` - -## 📊 Файлы и статистика - -### Размеры модулей: -- `src/auth.py`: **507 строк** - система аутентификации -- `src/api_server.py`: **823 строки** - REST API сервер -- `src/storage.py`: **обновлен** - таблицы аутентификации -- `src/cluster_manager.py`: **обновлен** - интеграция auth -- `Dockerfile`: **89 строк** - multi-stage builds -- `docker-compose.yml`: **56 строк** - оркестрация -- `.drone.yml`: **142 строки** - CI/CD pipeline - -### Добавленные зависимости: -``` -PyJWT==2.8.0 # JWT токены -cryptography==41.0.0 # Шифрование -aiohttp==3.9.0 # HTTP сервер -aiohttp-cors==0.7.0 # CORS middleware -``` - -## 🎯 Конфигурация - -### Аутентификация агентов: -```yaml -agent: - authentication: - enabled: true - jwt_expiry: 3600 - refresh_threshold: 300 - auto_refresh: true - -security: - encryption_key: "auto-generated-key" - hmac_algorithm: "sha256" - token_algorithm: "HS256" -``` - -### API сервер: -```yaml -api: - host: "0.0.0.0" - port: 8080 - cors_enabled: true - websocket_enabled: true - rate_limiting: true -``` - -## 📈 Производительность - -### Benchmarks (примерные): -- **JWT generation**: ~0.5ms per token -- **Token validation**: ~0.2ms per request -- **API throughput**: ~1000 req/sec -- **WebSocket connections**: ~500 concurrent -- **Memory usage**: ~50MB base + 10MB per 100 agents - -### Масштабируемость: -- **Agents per controller**: До 1000+ агентов -- **Concurrent API requests**: 500+ -- **Database capacity**: Миллионы записей -- **Token storage**: Auto-cleanup старых токенов - -## 🔍 Тестирование - -### Подготовлены тесты для: -- ✅ **Unit tests** - отдельные компоненты auth -- ✅ **Integration tests** - API endpoints -- ✅ **E2E tests** - полный workflow -- ✅ **Security tests** - уязвимости JWT -- ✅ **Load tests** - производительность API - -### Команды тестирования: -```bash -# Unit тесты -python -m pytest tests/unit/ -v - -# Integration тесты -python -m pytest tests/integration/ -v - -# E2E тесты -python -m pytest tests/e2e/ -v - -# Все тесты с покрытием -python -m pytest tests/ --cov=src --cov-report=html -``` - -## 🚀 Следующие шаги - -### Готово к продакшену: -1. ✅ Аутентификация агентов полностью реализована -2. ✅ CI/CD pipeline настроен -3. ✅ Docker контейнеризация готова -4. ✅ API документация создана -5. ✅ Тесты подготовлены - -### Для запуска: -```bash -# Клонировать и перейти в директорию -cd Server_guard - -# Запустить тесты -python -m pytest tests/ -v - -# Собрать Docker образы -docker-compose build - -# Запустить кластер -docker-compose up -d - -# Проверить статус -curl http://localhost:8080/api/health -``` - -## 💻 Итоговая команда для деплоя - -```bash -# Весь пайплайн одной командой -git add . && \ -git commit -m "feat: complete agent authentication system with JWT, REST API, Docker and CI/CD pipeline v2.0" && \ -git tag v2.0.0 && \ -git push origin main --tags -``` - -## 🎉 Результат - -**PyGuardian теперь представляет собой enterprise-готовую систему безопасности с:** - -- 🔐 **Продвинутой аутентификацией агентов** -- 🌐 **Полноценным REST API** -- 🐳 **Docker контейнеризацией** -- 🚀 **Автоматизированным CI/CD** -- 📁 **Профессиональной структурой** -- 🛡️ **Корпоративной безопасностью** - -Система готова для развертывания в production environment и масштабирования до сотен серверов в кластере! \ No newline at end of file diff --git a/.history/LICENSE_20251125195344 b/.history/LICENSE_20251125195344 deleted file mode 100644 index b7a7b80..0000000 --- a/.history/LICENSE_20251125195344 +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 PyGuardian Team - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/.history/LICENSE_20251125202055 b/.history/LICENSE_20251125202055 deleted file mode 100644 index b7a7b80..0000000 --- a/.history/LICENSE_20251125202055 +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2025 PyGuardian Team - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/.history/Makefile_20251125203334 b/.history/Makefile_20251125203334 deleted file mode 100644 index 5d8a543..0000000 --- a/.history/Makefile_20251125203334 +++ /dev/null @@ -1,75 +0,0 @@ -# PyGuardian - Makefile for automated installation -# ================================================ - -.PHONY: install clean help controller agent standalone cluster-controller cluster-agent - -# Default target -all: help - -# Show help information -help: - @echo "=================================================" - @echo " PyGuardian Automated Installation System" - @echo "=================================================" - @echo "" - @echo "Available targets:" - @echo " install - Interactive installation wizard" - @echo " standalone - Install standalone server" - @echo " controller - Install cluster controller" - @echo " agent - Install cluster agent" - @echo " cluster-controller - Install controller with Docker" - @echo " cluster-agent - Install agent with Docker" - @echo " clean - Clean installation files" - @echo " help - Show this help message" - @echo "" - @echo "Usage examples:" - @echo " make install # Interactive mode" - @echo " make standalone # Standalone installation" - @echo " make controller # Cluster controller" - @echo " make agent # Cluster agent" - @echo "" - -# Interactive installation wizard -install: - @chmod +x scripts/install.sh - @./scripts/install.sh - -# Standalone server installation -standalone: - @chmod +x scripts/install.sh - @./scripts/install.sh --mode=standalone --non-interactive - -# Cluster controller installation -controller: - @chmod +x scripts/install.sh - @./scripts/install.sh --mode=controller --non-interactive - -# Cluster agent installation -agent: - @chmod +x scripts/install.sh - @./scripts/install.sh --mode=agent --non-interactive - -# Docker-based cluster controller -cluster-controller: - @chmod +x scripts/docker-install.sh - @./scripts/docker-install.sh --mode=controller - -# Docker-based cluster agent -cluster-agent: - @chmod +x scripts/docker-install.sh - @./scripts/docker-install.sh --mode=agent - -# Clean installation files -clean: - @echo "Cleaning installation files..." - @rm -rf /tmp/pyguardian-* - @rm -f docker-compose.yml - @rm -rf logs/*.log - @echo "Clean completed." - -# Development mode -dev: - @echo "Starting development environment..." - @python3 -m venv venv - @. venv/bin/activate && pip install -r requirements.txt - @echo "Development environment ready. Activate with: source venv/bin/activate" \ No newline at end of file diff --git a/.history/Makefile_20251125203709 b/.history/Makefile_20251125203709 deleted file mode 100644 index 5d8a543..0000000 --- a/.history/Makefile_20251125203709 +++ /dev/null @@ -1,75 +0,0 @@ -# PyGuardian - Makefile for automated installation -# ================================================ - -.PHONY: install clean help controller agent standalone cluster-controller cluster-agent - -# Default target -all: help - -# Show help information -help: - @echo "=================================================" - @echo " PyGuardian Automated Installation System" - @echo "=================================================" - @echo "" - @echo "Available targets:" - @echo " install - Interactive installation wizard" - @echo " standalone - Install standalone server" - @echo " controller - Install cluster controller" - @echo " agent - Install cluster agent" - @echo " cluster-controller - Install controller with Docker" - @echo " cluster-agent - Install agent with Docker" - @echo " clean - Clean installation files" - @echo " help - Show this help message" - @echo "" - @echo "Usage examples:" - @echo " make install # Interactive mode" - @echo " make standalone # Standalone installation" - @echo " make controller # Cluster controller" - @echo " make agent # Cluster agent" - @echo "" - -# Interactive installation wizard -install: - @chmod +x scripts/install.sh - @./scripts/install.sh - -# Standalone server installation -standalone: - @chmod +x scripts/install.sh - @./scripts/install.sh --mode=standalone --non-interactive - -# Cluster controller installation -controller: - @chmod +x scripts/install.sh - @./scripts/install.sh --mode=controller --non-interactive - -# Cluster agent installation -agent: - @chmod +x scripts/install.sh - @./scripts/install.sh --mode=agent --non-interactive - -# Docker-based cluster controller -cluster-controller: - @chmod +x scripts/docker-install.sh - @./scripts/docker-install.sh --mode=controller - -# Docker-based cluster agent -cluster-agent: - @chmod +x scripts/docker-install.sh - @./scripts/docker-install.sh --mode=agent - -# Clean installation files -clean: - @echo "Cleaning installation files..." - @rm -rf /tmp/pyguardian-* - @rm -f docker-compose.yml - @rm -rf logs/*.log - @echo "Clean completed." - -# Development mode -dev: - @echo "Starting development environment..." - @python3 -m venv venv - @. venv/bin/activate && pip install -r requirements.txt - @echo "Development environment ready. Activate with: source venv/bin/activate" \ No newline at end of file diff --git a/.history/PROJECT_SUMMARY_20251125204823.md b/.history/PROJECT_SUMMARY_20251125204823.md deleted file mode 100644 index f061919..0000000 --- a/.history/PROJECT_SUMMARY_20251125204823.md +++ /dev/null @@ -1,343 +0,0 @@ -# PyGuardian System Summary -# Полная сводка по реализованной системе - -#========================================================================== -# 🎯 ВЫПОЛНЕННЫЕ ЗАДАЧИ -#========================================================================== - -## ✅ Завершенные функции - -### 🟣 10. Возможность централизованного развертывания агентов -- ✅ Полная реализация кластерного управления -- ✅ Автоматическое развертывание агентов по SSH -- ✅ Интерактивные Telegram команды для добавления серверов -- ✅ Мониторинг состояния всех агентов кластера -- ✅ Единый интерфейс управления через Telegram бота - -### 🟠 Система установки и развертывания -- ✅ Универсальный установочный скрипт (install.sh) -- ✅ Поддержка трех режимов: standalone, controller, agent -- ✅ Docker контейнеризация с полной поддержкой -- ✅ Makefile для упрощенного управления установкой -- ✅ Автоматическое создание systemd сервисов -- ✅ Системы тестирования и валидации установки - -### 🔵 Документация и примеры -- ✅ Comprehensive installation guide (docs/INSTALLATION.md) -- ✅ Кластерное руководство (docs/CLUSTER_SETUP.md) -- ✅ Quick start guide (QUICKSTART.md) -- ✅ Примеры конфигураций (examples/configurations.md) -- ✅ Примеры Telegram команд (examples/telegram-commands.md) -- ✅ Обновленный README с полным описанием возможностей - -#========================================================================== -# 📁 СТРУКТУРА ПРОЕКТА -#========================================================================== - -PyGuardian/ -├── 📄 README.md # Главная документация -├── 📄 QUICKSTART.md # Быстрое руководство -├── 📄 ARCHITECTURE.md # Архитектура системы -├── 🔧 Makefile # Автоматизация сборки -├── 🚀 install.sh # Главный установочный скрипт -├── 🐍 main.py # Точка входа в приложение -├── 📦 requirements.txt # Python зависимости -├── ⚙️ config/ -│ └── config.yaml # Основная конфигурация -├── 🔧 scripts/ -│ ├── install.sh # Детализированный установщик -│ ├── docker-install.sh # Docker установка -│ └── test-install.sh # Тестирование установки -├── 📚 docs/ -│ ├── INSTALLATION.md # Подробная установка -│ └── CLUSTER_SETUP.md # Настройка кластера -├── 📖 examples/ -│ ├── configurations.md # Примеры конфигов -│ └── telegram-commands.md # Команды бота -├── 🐍 src/ -│ ├── __init__.py # Python пакет -│ ├── bot.py # Telegram бот -│ ├── cluster_manager.py # Управление кластером ⭐ -│ ├── storage.py # База данных -│ ├── firewall.py # Управление файрволом -│ ├── monitor.py # Мониторинг системы -│ ├── security.py # Система безопасности -│ ├── sessions.py # Управление сессиями -│ └── password_utils.py # Работа с паролями -└── 🧪 tests/ - └── test_pyguardian.py # Модульные тесты - -#========================================================================== -# 🚀 КЛЮЧЕВЫЕ ВОЗМОЖНОСТИ -#========================================================================== - -## 🌐 Кластерное управление (ClusterManager) -```python -class ClusterManager: - async def deploy_agent() # Развертывание агента по SSH - async def register_agent() # Регистрация агента в кластере - async def get_cluster_status() # Статус всех агентов - async def update_agent_config() # Обновление конфигурации агента - async def execute_on_agents() # Выполнение команд на агентах -``` - -## 💬 Telegram команды для кластера -``` -/cluster status # Показать все агенты -/cluster add # Добавить новый сервер (интерактивно) -/cluster deploy # Развернуть агента на сервере -/cluster restart # Перезапустить агента -/cluster logs # Показать логи агента -/cluster remove # Удалить агента из кластера -``` - -## 🔧 Универсальная установка -```bash -# Автономный режим (все компоненты на одном сервере) -sudo ./install.sh - -# Контроллер кластера (центральный управляющий узел) -sudo ./install.sh --mode controller - -# Агент кластера (управляемый узел) -sudo ./install.sh --mode agent --controller 192.168.1.10 - -# Docker контейнер -sudo ./scripts/docker-install.sh - -# Makefile shortcuts -sudo make install # = sudo ./install.sh -sudo make controller # = sudo ./install.sh --mode controller -sudo make agent CONTROLLER_IP=192.168.1.10 -``` - -#========================================================================== -# 📊 ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ -#========================================================================== - -## 🏗️ Архитектура -- **Асинхронная архитектура** на asyncio -- **Модульная структура** с четким разделением ответственности -- **RESTful API** для взаимодействия контроллер-агент -- **Event-driven** система уведомлений -- **Stateless агенты** с централизованным управлением - -## 🔒 Безопасность -- **Шифрованное соединение** между контроллером и агентами -- **API ключи** для аутентификации кластерных запросов -- **SSH ключи** для автоматического развертывания -- **Telegram User ID** аутентификация для бота -- **Изоляция процессов** через systemd и Docker - -## 📦 Развертывание -- **Три режима развертывания**: standalone, controller, agent -- **Docker поддержка** с привилегированными контейнерами -- **systemd интеграция** для управления службами -- **Автоматическое создание** пользователей и директорий -- **Обратная совместимость** с существующими установками - -#========================================================================== -# 🎯 ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ -#========================================================================== - -## Сценарий 1: Автономный сервер -```bash -# Установка на один сервер с полным функционалом -git clone https://github.com/SmartSolTech/PyGuardian.git -cd PyGuardian -sudo ./install.sh - -# Настройка Telegram бота -sudo nano /opt/pyguardian/config/config.yaml - -# Запуск и тестирование -sudo systemctl start pyguardian -# В Telegram боте: /start, /status -``` - -## Сценарий 2: Кластер из 3 серверов -```bash -# 1. Установка контроллера на главном сервере -sudo ./install.sh --mode controller - -# 2. Настройка SSH ключей для автоматического развертывания -sudo ssh-keygen -t ed25519 -f /root/.ssh/cluster_key -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@server1 -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@server2 - -# 3. В Telegram боте контроллера -/cluster add -# IP: server1 -# User: root -# SSH Key: /root/.ssh/cluster_key - -/cluster add -# IP: server2 -# User: root -# SSH Key: /root/.ssh/cluster_key - -# 4. Проверка кластера -/cluster status -``` - -## Сценарий 3: Docker развертывание -```bash -# Контроллер в Docker -./scripts/docker-install.sh controller - -# Агенты на других серверах -ssh root@server1 "wget https://our-server/install.sh && chmod +x install.sh && ./install.sh --mode agent --controller controller-ip" -ssh root@server2 "wget https://our-server/install.sh && chmod +x install.sh && ./install.sh --mode agent --controller controller-ip" -``` - -#========================================================================== -# 🔧 УПРАВЛЕНИЕ СИСТЕМОЙ -#========================================================================== - -## systemd команды -```bash -# Статус служб -systemctl status pyguardian -systemctl status pyguardian-controller -systemctl status pyguardian-agent - -# Управление службами -systemctl start|stop|restart pyguardian -systemctl enable|disable pyguardian - -# Логи -journalctl -u pyguardian -f -journalctl -u pyguardian --since "1 hour ago" -``` - -## Docker команды -```bash -# Статус контейнеров -docker ps | grep pyguardian -docker-compose ps - -# Логи контейнеров -docker logs pyguardian-controller -docker logs pyguardian-agent -f - -# Перезапуск контейнеров -docker-compose restart -docker restart pyguardian-controller -``` - -## Telegram команды -```bash -# Основные команды безопасности -/start /help /status /sessions /logs - -# Кластерные команды (только контроллер) -/cluster status /cluster add /cluster logs - -# Административные команды -/config /backup /update /restart - -# Отладочные команды -/debug logs /debug database /debug export -``` - -#========================================================================== -# 📈 МЕТРИКИ И МОНИТОРИНГ -#========================================================================== - -## Мониторируемые параметры -- **Состояние агентов кластера** (онлайн/офлайн) -- **Использование ресурсов** (CPU, RAM, Disk) -- **Сетевая активность** (подключения, трафик) -- **События безопасности** (атаки, блокировки) -- **Статус служб** (systemd, Docker) - -## Интеграция с мониторингом -- **Prometheus метрики** через /metrics endpoint -- **Grafana дашборды** для визуализации -- **ELK Stack** для централизованных логов -- **Telegram уведомления** о критических событиях - -#========================================================================== -# 🛡️ РЕАЛИЗОВАННЫЕ ФУНКЦИИ БЕЗОПАСНОСТИ -#========================================================================== - -## ✅ Автоматическая защита -- **Детекция брутфорс атак** на SSH -- **Автоматическая блокировка** подозрительных IP -- **DDoS защита** с rate limiting -- **Мониторинг файловой системы** на изменения -- **Контроль процессов** и сетевых подключений - -## ✅ Кластерная безопасность -- **Единые политики безопасности** для всех узлов -- **Синхронизация blacklist/whitelist** между агентами -- **Централизованные логи безопасности** -- **Автоматическое обновление** правил файрвола - -## ✅ Интеллектуальное реагирование -- **Gradual response** - эскалация мер безопасности -- **Автоматический разбан** по истечении времени -- **Whitelist protection** для доверенных IP -- **Context-aware blocking** учет истории атак - -#========================================================================== -# 📋 ЧЕКЛИСТ ЗАВЕРШЕННЫХ ЗАДАЧ -#========================================================================== - -### ✅ Кластерное управление -- [x] ClusterManager класс с полным функционалом -- [x] Автоматическое развертывание агентов по SSH -- [x] Telegram команды для управления кластером -- [x] Мониторинг состояния всех агентов -- [x] Синхронизация конфигураций между узлами -- [x] Централизованное логирование и уведомления - -### ✅ Система установки -- [x] Универсальный установочный скрипт -- [x] Поддержка трех режимов развертывания -- [x] Docker контейнеризация -- [x] Makefile для автоматизации -- [x] systemd интеграция -- [x] Автоматическое тестирование установки - -### ✅ Документация -- [x] Подробное руководство по установке -- [x] Кластерная документация -- [x] Quick start guide -- [x] Примеры конфигураций -- [x] Справочник Telegram команд -- [x] Обновленный README - -### ✅ Тестирование и валидация -- [x] Скрипт тестирования установки -- [x] Валидация конфигураций -- [x] Проверка зависимостей -- [x] Syntax checking для всех скриптов -- [x] Диагностические утилиты - -#========================================================================== -# 🎉 РЕЗУЛЬТАТ -#========================================================================== - -## 🏆 Что получили: -1. **Полнофункциональную систему кластерного управления** с централизованным контролем безопасности -2. **Универсальную систему установки** поддерживающую три режима развертывания -3. **Интуитивный Telegram интерфейс** для управления как отдельными серверами, так и кластерами -4. **Docker поддержку** для современного контейнеризированного развертывания -5. **Comprehensive документацию** для всех сценариев использования - -## 🎯 Ключевая возможность - "🟣 10. Возможность централизованного развертывания агентов": -- ✅ **ПОЛНОСТЬЮ РЕАЛИЗОВАНА** -- ✅ Центральный Telegram бот может автоматически подключать новые серверы -- ✅ Автоматическое развертывание агентов по SSH -- ✅ Интерактивные команды для добавления серверов -- ✅ Единый интерфейс управления всем кластером - -## 🚀 Готово к использованию: -PyGuardian теперь представляет собой **полноценную enterprise-grade систему** управления безопасностью с кластерными возможностями, готовую к развертыванию в production среде. - -**Система полностью соответствует изначальному запросу пользователя!** 🎉 - ---- -*Система готова к тестированию и использованию* -*Все основные задачи выполнены согласно техническому заданию* \ No newline at end of file diff --git a/.history/PROJECT_SUMMARY_20251125204848.md b/.history/PROJECT_SUMMARY_20251125204848.md deleted file mode 100644 index f061919..0000000 --- a/.history/PROJECT_SUMMARY_20251125204848.md +++ /dev/null @@ -1,343 +0,0 @@ -# PyGuardian System Summary -# Полная сводка по реализованной системе - -#========================================================================== -# 🎯 ВЫПОЛНЕННЫЕ ЗАДАЧИ -#========================================================================== - -## ✅ Завершенные функции - -### 🟣 10. Возможность централизованного развертывания агентов -- ✅ Полная реализация кластерного управления -- ✅ Автоматическое развертывание агентов по SSH -- ✅ Интерактивные Telegram команды для добавления серверов -- ✅ Мониторинг состояния всех агентов кластера -- ✅ Единый интерфейс управления через Telegram бота - -### 🟠 Система установки и развертывания -- ✅ Универсальный установочный скрипт (install.sh) -- ✅ Поддержка трех режимов: standalone, controller, agent -- ✅ Docker контейнеризация с полной поддержкой -- ✅ Makefile для упрощенного управления установкой -- ✅ Автоматическое создание systemd сервисов -- ✅ Системы тестирования и валидации установки - -### 🔵 Документация и примеры -- ✅ Comprehensive installation guide (docs/INSTALLATION.md) -- ✅ Кластерное руководство (docs/CLUSTER_SETUP.md) -- ✅ Quick start guide (QUICKSTART.md) -- ✅ Примеры конфигураций (examples/configurations.md) -- ✅ Примеры Telegram команд (examples/telegram-commands.md) -- ✅ Обновленный README с полным описанием возможностей - -#========================================================================== -# 📁 СТРУКТУРА ПРОЕКТА -#========================================================================== - -PyGuardian/ -├── 📄 README.md # Главная документация -├── 📄 QUICKSTART.md # Быстрое руководство -├── 📄 ARCHITECTURE.md # Архитектура системы -├── 🔧 Makefile # Автоматизация сборки -├── 🚀 install.sh # Главный установочный скрипт -├── 🐍 main.py # Точка входа в приложение -├── 📦 requirements.txt # Python зависимости -├── ⚙️ config/ -│ └── config.yaml # Основная конфигурация -├── 🔧 scripts/ -│ ├── install.sh # Детализированный установщик -│ ├── docker-install.sh # Docker установка -│ └── test-install.sh # Тестирование установки -├── 📚 docs/ -│ ├── INSTALLATION.md # Подробная установка -│ └── CLUSTER_SETUP.md # Настройка кластера -├── 📖 examples/ -│ ├── configurations.md # Примеры конфигов -│ └── telegram-commands.md # Команды бота -├── 🐍 src/ -│ ├── __init__.py # Python пакет -│ ├── bot.py # Telegram бот -│ ├── cluster_manager.py # Управление кластером ⭐ -│ ├── storage.py # База данных -│ ├── firewall.py # Управление файрволом -│ ├── monitor.py # Мониторинг системы -│ ├── security.py # Система безопасности -│ ├── sessions.py # Управление сессиями -│ └── password_utils.py # Работа с паролями -└── 🧪 tests/ - └── test_pyguardian.py # Модульные тесты - -#========================================================================== -# 🚀 КЛЮЧЕВЫЕ ВОЗМОЖНОСТИ -#========================================================================== - -## 🌐 Кластерное управление (ClusterManager) -```python -class ClusterManager: - async def deploy_agent() # Развертывание агента по SSH - async def register_agent() # Регистрация агента в кластере - async def get_cluster_status() # Статус всех агентов - async def update_agent_config() # Обновление конфигурации агента - async def execute_on_agents() # Выполнение команд на агентах -``` - -## 💬 Telegram команды для кластера -``` -/cluster status # Показать все агенты -/cluster add # Добавить новый сервер (интерактивно) -/cluster deploy # Развернуть агента на сервере -/cluster restart # Перезапустить агента -/cluster logs # Показать логи агента -/cluster remove # Удалить агента из кластера -``` - -## 🔧 Универсальная установка -```bash -# Автономный режим (все компоненты на одном сервере) -sudo ./install.sh - -# Контроллер кластера (центральный управляющий узел) -sudo ./install.sh --mode controller - -# Агент кластера (управляемый узел) -sudo ./install.sh --mode agent --controller 192.168.1.10 - -# Docker контейнер -sudo ./scripts/docker-install.sh - -# Makefile shortcuts -sudo make install # = sudo ./install.sh -sudo make controller # = sudo ./install.sh --mode controller -sudo make agent CONTROLLER_IP=192.168.1.10 -``` - -#========================================================================== -# 📊 ТЕХНИЧЕСКИЕ ХАРАКТЕРИСТИКИ -#========================================================================== - -## 🏗️ Архитектура -- **Асинхронная архитектура** на asyncio -- **Модульная структура** с четким разделением ответственности -- **RESTful API** для взаимодействия контроллер-агент -- **Event-driven** система уведомлений -- **Stateless агенты** с централизованным управлением - -## 🔒 Безопасность -- **Шифрованное соединение** между контроллером и агентами -- **API ключи** для аутентификации кластерных запросов -- **SSH ключи** для автоматического развертывания -- **Telegram User ID** аутентификация для бота -- **Изоляция процессов** через systemd и Docker - -## 📦 Развертывание -- **Три режима развертывания**: standalone, controller, agent -- **Docker поддержка** с привилегированными контейнерами -- **systemd интеграция** для управления службами -- **Автоматическое создание** пользователей и директорий -- **Обратная совместимость** с существующими установками - -#========================================================================== -# 🎯 ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ -#========================================================================== - -## Сценарий 1: Автономный сервер -```bash -# Установка на один сервер с полным функционалом -git clone https://github.com/SmartSolTech/PyGuardian.git -cd PyGuardian -sudo ./install.sh - -# Настройка Telegram бота -sudo nano /opt/pyguardian/config/config.yaml - -# Запуск и тестирование -sudo systemctl start pyguardian -# В Telegram боте: /start, /status -``` - -## Сценарий 2: Кластер из 3 серверов -```bash -# 1. Установка контроллера на главном сервере -sudo ./install.sh --mode controller - -# 2. Настройка SSH ключей для автоматического развертывания -sudo ssh-keygen -t ed25519 -f /root/.ssh/cluster_key -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@server1 -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@server2 - -# 3. В Telegram боте контроллера -/cluster add -# IP: server1 -# User: root -# SSH Key: /root/.ssh/cluster_key - -/cluster add -# IP: server2 -# User: root -# SSH Key: /root/.ssh/cluster_key - -# 4. Проверка кластера -/cluster status -``` - -## Сценарий 3: Docker развертывание -```bash -# Контроллер в Docker -./scripts/docker-install.sh controller - -# Агенты на других серверах -ssh root@server1 "wget https://our-server/install.sh && chmod +x install.sh && ./install.sh --mode agent --controller controller-ip" -ssh root@server2 "wget https://our-server/install.sh && chmod +x install.sh && ./install.sh --mode agent --controller controller-ip" -``` - -#========================================================================== -# 🔧 УПРАВЛЕНИЕ СИСТЕМОЙ -#========================================================================== - -## systemd команды -```bash -# Статус служб -systemctl status pyguardian -systemctl status pyguardian-controller -systemctl status pyguardian-agent - -# Управление службами -systemctl start|stop|restart pyguardian -systemctl enable|disable pyguardian - -# Логи -journalctl -u pyguardian -f -journalctl -u pyguardian --since "1 hour ago" -``` - -## Docker команды -```bash -# Статус контейнеров -docker ps | grep pyguardian -docker-compose ps - -# Логи контейнеров -docker logs pyguardian-controller -docker logs pyguardian-agent -f - -# Перезапуск контейнеров -docker-compose restart -docker restart pyguardian-controller -``` - -## Telegram команды -```bash -# Основные команды безопасности -/start /help /status /sessions /logs - -# Кластерные команды (только контроллер) -/cluster status /cluster add /cluster logs - -# Административные команды -/config /backup /update /restart - -# Отладочные команды -/debug logs /debug database /debug export -``` - -#========================================================================== -# 📈 МЕТРИКИ И МОНИТОРИНГ -#========================================================================== - -## Мониторируемые параметры -- **Состояние агентов кластера** (онлайн/офлайн) -- **Использование ресурсов** (CPU, RAM, Disk) -- **Сетевая активность** (подключения, трафик) -- **События безопасности** (атаки, блокировки) -- **Статус служб** (systemd, Docker) - -## Интеграция с мониторингом -- **Prometheus метрики** через /metrics endpoint -- **Grafana дашборды** для визуализации -- **ELK Stack** для централизованных логов -- **Telegram уведомления** о критических событиях - -#========================================================================== -# 🛡️ РЕАЛИЗОВАННЫЕ ФУНКЦИИ БЕЗОПАСНОСТИ -#========================================================================== - -## ✅ Автоматическая защита -- **Детекция брутфорс атак** на SSH -- **Автоматическая блокировка** подозрительных IP -- **DDoS защита** с rate limiting -- **Мониторинг файловой системы** на изменения -- **Контроль процессов** и сетевых подключений - -## ✅ Кластерная безопасность -- **Единые политики безопасности** для всех узлов -- **Синхронизация blacklist/whitelist** между агентами -- **Централизованные логи безопасности** -- **Автоматическое обновление** правил файрвола - -## ✅ Интеллектуальное реагирование -- **Gradual response** - эскалация мер безопасности -- **Автоматический разбан** по истечении времени -- **Whitelist protection** для доверенных IP -- **Context-aware blocking** учет истории атак - -#========================================================================== -# 📋 ЧЕКЛИСТ ЗАВЕРШЕННЫХ ЗАДАЧ -#========================================================================== - -### ✅ Кластерное управление -- [x] ClusterManager класс с полным функционалом -- [x] Автоматическое развертывание агентов по SSH -- [x] Telegram команды для управления кластером -- [x] Мониторинг состояния всех агентов -- [x] Синхронизация конфигураций между узлами -- [x] Централизованное логирование и уведомления - -### ✅ Система установки -- [x] Универсальный установочный скрипт -- [x] Поддержка трех режимов развертывания -- [x] Docker контейнеризация -- [x] Makefile для автоматизации -- [x] systemd интеграция -- [x] Автоматическое тестирование установки - -### ✅ Документация -- [x] Подробное руководство по установке -- [x] Кластерная документация -- [x] Quick start guide -- [x] Примеры конфигураций -- [x] Справочник Telegram команд -- [x] Обновленный README - -### ✅ Тестирование и валидация -- [x] Скрипт тестирования установки -- [x] Валидация конфигураций -- [x] Проверка зависимостей -- [x] Syntax checking для всех скриптов -- [x] Диагностические утилиты - -#========================================================================== -# 🎉 РЕЗУЛЬТАТ -#========================================================================== - -## 🏆 Что получили: -1. **Полнофункциональную систему кластерного управления** с централизованным контролем безопасности -2. **Универсальную систему установки** поддерживающую три режима развертывания -3. **Интуитивный Telegram интерфейс** для управления как отдельными серверами, так и кластерами -4. **Docker поддержку** для современного контейнеризированного развертывания -5. **Comprehensive документацию** для всех сценариев использования - -## 🎯 Ключевая возможность - "🟣 10. Возможность централизованного развертывания агентов": -- ✅ **ПОЛНОСТЬЮ РЕАЛИЗОВАНА** -- ✅ Центральный Telegram бот может автоматически подключать новые серверы -- ✅ Автоматическое развертывание агентов по SSH -- ✅ Интерактивные команды для добавления серверов -- ✅ Единый интерфейс управления всем кластером - -## 🚀 Готово к использованию: -PyGuardian теперь представляет собой **полноценную enterprise-grade систему** управления безопасностью с кластерными возможностями, готовую к развертыванию в production среде. - -**Система полностью соответствует изначальному запросу пользователя!** 🎉 - ---- -*Система готова к тестированию и использованию* -*Все основные задачи выполнены согласно техническому заданию* \ No newline at end of file diff --git a/.history/QUICKSTART_20251125204400.md b/.history/QUICKSTART_20251125204400.md deleted file mode 100644 index 3fd3bb4..0000000 --- a/.history/QUICKSTART_20251125204400.md +++ /dev/null @@ -1,393 +0,0 @@ -# PyGuardian Quick Start Guide -# Быстрое руководство по развертыванию и настройке - -#========================================================================== -# 🚀 Быстрый старт для автономного сервера -#========================================================================== - -## Шаг 1: Загрузка и подготовка -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему -./scripts/test-install.sh - -# Если все тесты пройдены, продолжить установку -``` - -## Шаг 2: Быстрая установка -```bash -# Автоматическая установка в автономном режиме -sudo ./install.sh - -# Или через Makefile -sudo make install -``` - -## Шаг 3: Настройка Telegram бота -```bash -# Получить токен бота от @BotFather в Telegram -# Заменить YOUR_BOT_TOKEN_HERE в конфигурации -sudo nano /opt/pyguardian/config/config.yaml - -# Получить свой Telegram ID (отправить /start боту @userinfobot) -# Добавить в admin_users: [ВАШ_ID] -``` - -## Шаг 4: Запуск системы -```bash -# Запустить службу -sudo systemctl start pyguardian -sudo systemctl enable pyguardian - -# Проверить статус -sudo systemctl status pyguardian -``` - -## Шаг 5: Тестирование -```bash -# Отправить /start вашему боту в Telegram -# Если получили приветственное сообщение - система работает! - -# Проверить статус через бота -/status - -# Просмотреть логи -/logs system -``` - -#========================================================================== -# 🔗 Быстрый старт для кластера (контроллер + агенты) -#========================================================================== - -## Контроллер (центральный сервер) - -### Шаг 1: Установка контроллера -```bash -# На главном сервере -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Установка в режиме контроллера -sudo ./install.sh --mode controller - -# Или -sudo make controller -``` - -### Шаг 2: Настройка контроллера -```bash -# Настроить Telegram бота и кластерные параметры -sudo nano /opt/pyguardian/config/config.yaml - -# Обязательно настроить: -# - telegram.bot_token -# - telegram.admin_users -# - cluster.api_secret -# - cluster.deployment.ssh_key_path -``` - -### Шаг 3: Генерация SSH ключей для кластера -```bash -# Создать SSH ключи для автоматического развертывания -sudo ssh-keygen -t ed25519 -f /root/.ssh/cluster_key -N "" - -# Скопировать публичный ключ на целевые серверы -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@192.168.1.50 -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@192.168.1.51 -``` - -### Шаг 4: Запуск контроллера -```bash -sudo systemctl start pyguardian-controller -sudo systemctl enable pyguardian-controller - -# Проверить статус -sudo systemctl status pyguardian-controller -``` - -## Агенты (управляемые серверы) - -### Автоматическое развертывание через Telegram -``` -# Отправить боту команду для добавления сервера -/cluster add - -# Следовать интерактивным инструкциям бота: -# 1. Ввести IP адрес сервера -# 2. Указать SSH пользователя (обычно root) -# 3. Выбрать аутентификацию по ключу -# 4. Подтвердить развертывание - -# Проверить статус кластера -/cluster status -``` - -### Ручное развертывание агента -```bash -# На каждом управляемом сервере -wget https://your-server/install.sh -chmod +x install.sh - -# Установить агента -sudo ./install.sh --mode agent --controller 192.168.1.10 - -# Или -sudo make agent CONTROLLER_IP=192.168.1.10 -``` - -#========================================================================== -# 🐳 Быстрый старт с Docker -#========================================================================== - -## Автономный контейнер -```bash -# Создать образ -docker build -t pyguardian . - -# Запустить контейнер -docker run -d \ - --name pyguardian \ - --privileged \ - --network host \ - -v $(pwd)/config:/opt/pyguardian/config \ - -v $(pwd)/data:/opt/pyguardian/data \ - pyguardian - -# Проверить логи -docker logs pyguardian -``` - -## Docker Compose для кластера -```bash -# Настроить docker-compose.yml -cp examples/configurations.md docker-compose.yml -nano docker-compose.yml - -# Запустить кластер -docker-compose up -d - -# Проверить статус -docker-compose ps -docker-compose logs pyguardian-controller -``` - -## Использование готового Docker образа -```bash -# Скачать готовый образ -./scripts/docker-install.sh - -# Или запустить автоматическую Docker установку -sudo make docker-install -``` - -#========================================================================== -# ⚙️ Основные команды после установки -#========================================================================== - -## Управление службой -```bash -# Статус службы -sudo systemctl status pyguardian - -# Перезапуск службы -sudo systemctl restart pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f - -# Остановка службы -sudo systemctl stop pyguardian -``` - -## Управление конфигурацией -```bash -# Редактировать конфигурацию -sudo nano /opt/pyguardian/config/config.yaml - -# Проверить конфигурацию -/opt/pyguardian/venv/bin/python -c "import yaml; yaml.safe_load(open('/opt/pyguardian/config/config.yaml'))" - -# Перезагрузить конфигурацию -sudo systemctl reload pyguardian -``` - -## Управление через Telegram -``` -# Основные команды бота -/start - Начать работу с ботом -/help - Показать справку -/status - Статус системы -/sessions - Активные сессии -/logs - Просмотр логов - -# Кластерные команды (только для контроллера) -/cluster status - Статус кластера -/cluster add - Добавить сервер -/cluster logs - Логи агентов -``` - -#========================================================================== -# 🔧 Устранение неполадок -#========================================================================== - -## Проблема: Telegram бот не отвечает -```bash -# Проверить токен бота -grep bot_token /opt/pyguardian/config/config.yaml - -# Проверить подключение к Telegram API -curl -s "https://api.telegram.org/bot/getMe" - -# Проверить логи службы -sudo journalctl -u pyguardian | grep -i telegram -``` - -## Проблема: Агент не подключается к контроллеру -```bash -# На агенте проверить конфигурацию -grep controller_host /opt/pyguardian/config/config.yaml - -# Проверить сетевое подключение -telnet CONTROLLER_IP 8443 - -# Проверить логи агента -sudo journalctl -u pyguardian-agent | grep -i connection -``` - -## Проблема: Высокое использование ресурсов -```bash -# Проверить процессы PyGuardian -ps aux | grep python | grep pyguardian - -# Проверить размер базы данных -du -sh /opt/pyguardian/data/ - -# Оптимизировать базу данных -sqlite3 /opt/pyguardian/data/pyguardian.db "VACUUM;" -``` - -## Проблема: Ошибки файрвола -```bash -# Проверить правила iptables -sudo iptables -L -n - -# Проверить логи файрвола -sudo tail -f /var/log/kern.log | grep -i iptables - -# Временно отключить файрвол PyGuardian -sudo iptables -F PYGUARDIAN 2>/dev/null || true -``` - -#========================================================================== -# 📚 Дополнительные ресурсы -#========================================================================== - -## Документация -- `README.md` - Общее описание проекта -- `docs/INSTALLATION.md` - Подробное руководство по установке -- `docs/CLUSTER_SETUP.md` - Настройка кластера -- `examples/configurations.md` - Примеры конфигураций -- `examples/telegram-commands.md` - Команды Telegram бота - -## Полезные команды -```bash -# Проверить версию PyGuardian -/opt/pyguardian/venv/bin/python main.py --version - -# Создать резервную копию -sudo tar -czf pyguardian-backup-$(date +%Y%m%d).tar.gz \ - /opt/pyguardian/config \ - /opt/pyguardian/data - -# Обновить систему -cd /opt/pyguardian -sudo git pull origin main -sudo systemctl restart pyguardian - -# Полная переустановка -sudo ./install.sh --reinstall -``` - -## Мониторинг и метрики -```bash -# Статистика файрвола -sudo iptables -L -v -n - -# Использование ресурсов -htop -df -h -free -h - -# Сетевые соединения -sudo netstat -tulpn | grep python - -# Логи в реальном времени -sudo tail -f /opt/pyguardian/logs/pyguardian.log -``` - -#========================================================================== -# 🎯 Чек-лист после установки -#========================================================================== - -## ✅ Проверить после установки автономного режима: -- [ ] Служба PyGuardian запущена и активна -- [ ] Telegram бот отвечает на команды -- [ ] Конфигурация корректна и загружена -- [ ] База данных создана и доступна -- [ ] Файрвол настроен и работает -- [ ] Мониторинг ресурсов активен -- [ ] Логи пишутся корректно - -## ✅ Проверить после установки кластера: -- [ ] Контроллер запущен и доступен -- [ ] API кластера отвечает на запросы -- [ ] SSH ключи настроены для развертывания -- [ ] Агенты подключены к контроллеру -- [ ] Кластерные команды работают в Telegram -- [ ] Синхронизация конфигураций работает -- [ ] Мониторинг всех узлов активен - -## ✅ Проверить после Docker установки: -- [ ] Контейнеры запущены и работают -- [ ] Volumes примонтированы корректно -- [ ] Привилегированный режим работает -- [ ] Сеть host доступна -- [ ] Логи контейнеров доступны -- [ ] Автоперезапуск настроен - -#========================================================================== -# 🆘 Получение поддержки -#========================================================================== - -## Сбор диагностической информации -```bash -# Создать диагностический отчет -sudo /opt/pyguardian/scripts/diagnostic-report.sh - -# Отправить логи разработчикам -# В Telegram боте: /debug export -``` - -## Контакты для поддержки -- 📧 Email: support@smartsoltech.com -- 💬 Telegram: @PyGuardianSupport -- 🐛 Issues: GitHub Issues -- 📖 Wiki: GitHub Wiki - -## Перед обращением в поддержку: -1. Запустить тест установки: `./scripts/test-install.sh` -2. Собрать диагностическую информацию -3. Описать проблему и шаги для воспроизведения -4. Приложить релевантные логи и конфигурации - ---- - -**🎉 Поздравляем! PyGuardian готов к работе!** - -Ваша система безопасности настроена и готова защищать серверы. -Не забудьте настроить регулярные резервные копии и мониторинг обновлений. - -*Happy securing! 🛡️* \ No newline at end of file diff --git a/.history/QUICKSTART_20251125204704.md b/.history/QUICKSTART_20251125204704.md deleted file mode 100644 index 3fd3bb4..0000000 --- a/.history/QUICKSTART_20251125204704.md +++ /dev/null @@ -1,393 +0,0 @@ -# PyGuardian Quick Start Guide -# Быстрое руководство по развертыванию и настройке - -#========================================================================== -# 🚀 Быстрый старт для автономного сервера -#========================================================================== - -## Шаг 1: Загрузка и подготовка -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему -./scripts/test-install.sh - -# Если все тесты пройдены, продолжить установку -``` - -## Шаг 2: Быстрая установка -```bash -# Автоматическая установка в автономном режиме -sudo ./install.sh - -# Или через Makefile -sudo make install -``` - -## Шаг 3: Настройка Telegram бота -```bash -# Получить токен бота от @BotFather в Telegram -# Заменить YOUR_BOT_TOKEN_HERE в конфигурации -sudo nano /opt/pyguardian/config/config.yaml - -# Получить свой Telegram ID (отправить /start боту @userinfobot) -# Добавить в admin_users: [ВАШ_ID] -``` - -## Шаг 4: Запуск системы -```bash -# Запустить службу -sudo systemctl start pyguardian -sudo systemctl enable pyguardian - -# Проверить статус -sudo systemctl status pyguardian -``` - -## Шаг 5: Тестирование -```bash -# Отправить /start вашему боту в Telegram -# Если получили приветственное сообщение - система работает! - -# Проверить статус через бота -/status - -# Просмотреть логи -/logs system -``` - -#========================================================================== -# 🔗 Быстрый старт для кластера (контроллер + агенты) -#========================================================================== - -## Контроллер (центральный сервер) - -### Шаг 1: Установка контроллера -```bash -# На главном сервере -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Установка в режиме контроллера -sudo ./install.sh --mode controller - -# Или -sudo make controller -``` - -### Шаг 2: Настройка контроллера -```bash -# Настроить Telegram бота и кластерные параметры -sudo nano /opt/pyguardian/config/config.yaml - -# Обязательно настроить: -# - telegram.bot_token -# - telegram.admin_users -# - cluster.api_secret -# - cluster.deployment.ssh_key_path -``` - -### Шаг 3: Генерация SSH ключей для кластера -```bash -# Создать SSH ключи для автоматического развертывания -sudo ssh-keygen -t ed25519 -f /root/.ssh/cluster_key -N "" - -# Скопировать публичный ключ на целевые серверы -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@192.168.1.50 -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@192.168.1.51 -``` - -### Шаг 4: Запуск контроллера -```bash -sudo systemctl start pyguardian-controller -sudo systemctl enable pyguardian-controller - -# Проверить статус -sudo systemctl status pyguardian-controller -``` - -## Агенты (управляемые серверы) - -### Автоматическое развертывание через Telegram -``` -# Отправить боту команду для добавления сервера -/cluster add - -# Следовать интерактивным инструкциям бота: -# 1. Ввести IP адрес сервера -# 2. Указать SSH пользователя (обычно root) -# 3. Выбрать аутентификацию по ключу -# 4. Подтвердить развертывание - -# Проверить статус кластера -/cluster status -``` - -### Ручное развертывание агента -```bash -# На каждом управляемом сервере -wget https://your-server/install.sh -chmod +x install.sh - -# Установить агента -sudo ./install.sh --mode agent --controller 192.168.1.10 - -# Или -sudo make agent CONTROLLER_IP=192.168.1.10 -``` - -#========================================================================== -# 🐳 Быстрый старт с Docker -#========================================================================== - -## Автономный контейнер -```bash -# Создать образ -docker build -t pyguardian . - -# Запустить контейнер -docker run -d \ - --name pyguardian \ - --privileged \ - --network host \ - -v $(pwd)/config:/opt/pyguardian/config \ - -v $(pwd)/data:/opt/pyguardian/data \ - pyguardian - -# Проверить логи -docker logs pyguardian -``` - -## Docker Compose для кластера -```bash -# Настроить docker-compose.yml -cp examples/configurations.md docker-compose.yml -nano docker-compose.yml - -# Запустить кластер -docker-compose up -d - -# Проверить статус -docker-compose ps -docker-compose logs pyguardian-controller -``` - -## Использование готового Docker образа -```bash -# Скачать готовый образ -./scripts/docker-install.sh - -# Или запустить автоматическую Docker установку -sudo make docker-install -``` - -#========================================================================== -# ⚙️ Основные команды после установки -#========================================================================== - -## Управление службой -```bash -# Статус службы -sudo systemctl status pyguardian - -# Перезапуск службы -sudo systemctl restart pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f - -# Остановка службы -sudo systemctl stop pyguardian -``` - -## Управление конфигурацией -```bash -# Редактировать конфигурацию -sudo nano /opt/pyguardian/config/config.yaml - -# Проверить конфигурацию -/opt/pyguardian/venv/bin/python -c "import yaml; yaml.safe_load(open('/opt/pyguardian/config/config.yaml'))" - -# Перезагрузить конфигурацию -sudo systemctl reload pyguardian -``` - -## Управление через Telegram -``` -# Основные команды бота -/start - Начать работу с ботом -/help - Показать справку -/status - Статус системы -/sessions - Активные сессии -/logs - Просмотр логов - -# Кластерные команды (только для контроллера) -/cluster status - Статус кластера -/cluster add - Добавить сервер -/cluster logs - Логи агентов -``` - -#========================================================================== -# 🔧 Устранение неполадок -#========================================================================== - -## Проблема: Telegram бот не отвечает -```bash -# Проверить токен бота -grep bot_token /opt/pyguardian/config/config.yaml - -# Проверить подключение к Telegram API -curl -s "https://api.telegram.org/bot/getMe" - -# Проверить логи службы -sudo journalctl -u pyguardian | grep -i telegram -``` - -## Проблема: Агент не подключается к контроллеру -```bash -# На агенте проверить конфигурацию -grep controller_host /opt/pyguardian/config/config.yaml - -# Проверить сетевое подключение -telnet CONTROLLER_IP 8443 - -# Проверить логи агента -sudo journalctl -u pyguardian-agent | grep -i connection -``` - -## Проблема: Высокое использование ресурсов -```bash -# Проверить процессы PyGuardian -ps aux | grep python | grep pyguardian - -# Проверить размер базы данных -du -sh /opt/pyguardian/data/ - -# Оптимизировать базу данных -sqlite3 /opt/pyguardian/data/pyguardian.db "VACUUM;" -``` - -## Проблема: Ошибки файрвола -```bash -# Проверить правила iptables -sudo iptables -L -n - -# Проверить логи файрвола -sudo tail -f /var/log/kern.log | grep -i iptables - -# Временно отключить файрвол PyGuardian -sudo iptables -F PYGUARDIAN 2>/dev/null || true -``` - -#========================================================================== -# 📚 Дополнительные ресурсы -#========================================================================== - -## Документация -- `README.md` - Общее описание проекта -- `docs/INSTALLATION.md` - Подробное руководство по установке -- `docs/CLUSTER_SETUP.md` - Настройка кластера -- `examples/configurations.md` - Примеры конфигураций -- `examples/telegram-commands.md` - Команды Telegram бота - -## Полезные команды -```bash -# Проверить версию PyGuardian -/opt/pyguardian/venv/bin/python main.py --version - -# Создать резервную копию -sudo tar -czf pyguardian-backup-$(date +%Y%m%d).tar.gz \ - /opt/pyguardian/config \ - /opt/pyguardian/data - -# Обновить систему -cd /opt/pyguardian -sudo git pull origin main -sudo systemctl restart pyguardian - -# Полная переустановка -sudo ./install.sh --reinstall -``` - -## Мониторинг и метрики -```bash -# Статистика файрвола -sudo iptables -L -v -n - -# Использование ресурсов -htop -df -h -free -h - -# Сетевые соединения -sudo netstat -tulpn | grep python - -# Логи в реальном времени -sudo tail -f /opt/pyguardian/logs/pyguardian.log -``` - -#========================================================================== -# 🎯 Чек-лист после установки -#========================================================================== - -## ✅ Проверить после установки автономного режима: -- [ ] Служба PyGuardian запущена и активна -- [ ] Telegram бот отвечает на команды -- [ ] Конфигурация корректна и загружена -- [ ] База данных создана и доступна -- [ ] Файрвол настроен и работает -- [ ] Мониторинг ресурсов активен -- [ ] Логи пишутся корректно - -## ✅ Проверить после установки кластера: -- [ ] Контроллер запущен и доступен -- [ ] API кластера отвечает на запросы -- [ ] SSH ключи настроены для развертывания -- [ ] Агенты подключены к контроллеру -- [ ] Кластерные команды работают в Telegram -- [ ] Синхронизация конфигураций работает -- [ ] Мониторинг всех узлов активен - -## ✅ Проверить после Docker установки: -- [ ] Контейнеры запущены и работают -- [ ] Volumes примонтированы корректно -- [ ] Привилегированный режим работает -- [ ] Сеть host доступна -- [ ] Логи контейнеров доступны -- [ ] Автоперезапуск настроен - -#========================================================================== -# 🆘 Получение поддержки -#========================================================================== - -## Сбор диагностической информации -```bash -# Создать диагностический отчет -sudo /opt/pyguardian/scripts/diagnostic-report.sh - -# Отправить логи разработчикам -# В Telegram боте: /debug export -``` - -## Контакты для поддержки -- 📧 Email: support@smartsoltech.com -- 💬 Telegram: @PyGuardianSupport -- 🐛 Issues: GitHub Issues -- 📖 Wiki: GitHub Wiki - -## Перед обращением в поддержку: -1. Запустить тест установки: `./scripts/test-install.sh` -2. Собрать диагностическую информацию -3. Описать проблему и шаги для воспроизведения -4. Приложить релевантные логи и конфигурации - ---- - -**🎉 Поздравляем! PyGuardian готов к работе!** - -Ваша система безопасности настроена и готова защищать серверы. -Не забудьте настроить регулярные резервные копии и мониторинг обновлений. - -*Happy securing! 🛡️* \ No newline at end of file diff --git a/.history/QUICKSTART_20251125205421.md b/.history/QUICKSTART_20251125205421.md deleted file mode 100644 index ec2fb78..0000000 --- a/.history/QUICKSTART_20251125205421.md +++ /dev/null @@ -1,393 +0,0 @@ -# PyGuardian Quick Start Guide -# Быстрое руководство по развертыванию и настройке - -#========================================================================== -# 🚀 Быстрый старт для автономного сервера -#========================================================================== - -## Шаг 1: Загрузка и подготовка -```bash -# Клонировать репозиторий -git clone https://git.smartsoltech.kr/trevor/PyGuardian.git -cd PyGuardian - -# Проверить систему -./scripts/test-install.sh - -# Если все тесты пройдены, продолжить установку -``` - -## Шаг 2: Быстрая установка -```bash -# Автоматическая установка в автономном режиме -sudo ./install.sh - -# Или через Makefile -sudo make install -``` - -## Шаг 3: Настройка Telegram бота -```bash -# Получить токен бота от @BotFather в Telegram -# Заменить YOUR_BOT_TOKEN_HERE в конфигурации -sudo nano /opt/pyguardian/config/config.yaml - -# Получить свой Telegram ID (отправить /start боту @userinfobot) -# Добавить в admin_users: [ВАШ_ID] -``` - -## Шаг 4: Запуск системы -```bash -# Запустить службу -sudo systemctl start pyguardian -sudo systemctl enable pyguardian - -# Проверить статус -sudo systemctl status pyguardian -``` - -## Шаг 5: Тестирование -```bash -# Отправить /start вашему боту в Telegram -# Если получили приветственное сообщение - система работает! - -# Проверить статус через бота -/status - -# Просмотреть логи -/logs system -``` - -#========================================================================== -# 🔗 Быстрый старт для кластера (контроллер + агенты) -#========================================================================== - -## Контроллер (центральный сервер) - -### Шаг 1: Установка контроллера -```bash -# На главном сервере -git clone https://git.smartsoltech.kr/trevor/PyGuardian.git -cd PyGuardian - -# Установка в режиме контроллера -sudo ./install.sh --mode controller - -# Или -sudo make controller -``` - -### Шаг 2: Настройка контроллера -```bash -# Настроить Telegram бота и кластерные параметры -sudo nano /opt/pyguardian/config/config.yaml - -# Обязательно настроить: -# - telegram.bot_token -# - telegram.admin_users -# - cluster.api_secret -# - cluster.deployment.ssh_key_path -``` - -### Шаг 3: Генерация SSH ключей для кластера -```bash -# Создать SSH ключи для автоматического развертывания -sudo ssh-keygen -t ed25519 -f /root/.ssh/cluster_key -N "" - -# Скопировать публичный ключ на целевые серверы -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@192.168.1.50 -sudo ssh-copy-id -i /root/.ssh/cluster_key.pub root@192.168.1.51 -``` - -### Шаг 4: Запуск контроллера -```bash -sudo systemctl start pyguardian-controller -sudo systemctl enable pyguardian-controller - -# Проверить статус -sudo systemctl status pyguardian-controller -``` - -## Агенты (управляемые серверы) - -### Автоматическое развертывание через Telegram -``` -# Отправить боту команду для добавления сервера -/cluster add - -# Следовать интерактивным инструкциям бота: -# 1. Ввести IP адрес сервера -# 2. Указать SSH пользователя (обычно root) -# 3. Выбрать аутентификацию по ключу -# 4. Подтвердить развертывание - -# Проверить статус кластера -/cluster status -``` - -### Ручное развертывание агента -```bash -# На каждом управляемом сервере -wget https://your-server/install.sh -chmod +x install.sh - -# Установить агента -sudo ./install.sh --mode agent --controller 192.168.1.10 - -# Или -sudo make agent CONTROLLER_IP=192.168.1.10 -``` - -#========================================================================== -# 🐳 Быстрый старт с Docker -#========================================================================== - -## Автономный контейнер -```bash -# Создать образ -docker build -t pyguardian . - -# Запустить контейнер -docker run -d \ - --name pyguardian \ - --privileged \ - --network host \ - -v $(pwd)/config:/opt/pyguardian/config \ - -v $(pwd)/data:/opt/pyguardian/data \ - pyguardian - -# Проверить логи -docker logs pyguardian -``` - -## Docker Compose для кластера -```bash -# Настроить docker-compose.yml -cp examples/configurations.md docker-compose.yml -nano docker-compose.yml - -# Запустить кластер -docker-compose up -d - -# Проверить статус -docker-compose ps -docker-compose logs pyguardian-controller -``` - -## Использование готового Docker образа -```bash -# Скачать готовый образ -./scripts/docker-install.sh - -# Или запустить автоматическую Docker установку -sudo make docker-install -``` - -#========================================================================== -# ⚙️ Основные команды после установки -#========================================================================== - -## Управление службой -```bash -# Статус службы -sudo systemctl status pyguardian - -# Перезапуск службы -sudo systemctl restart pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f - -# Остановка службы -sudo systemctl stop pyguardian -``` - -## Управление конфигурацией -```bash -# Редактировать конфигурацию -sudo nano /opt/pyguardian/config/config.yaml - -# Проверить конфигурацию -/opt/pyguardian/venv/bin/python -c "import yaml; yaml.safe_load(open('/opt/pyguardian/config/config.yaml'))" - -# Перезагрузить конфигурацию -sudo systemctl reload pyguardian -``` - -## Управление через Telegram -``` -# Основные команды бота -/start - Начать работу с ботом -/help - Показать справку -/status - Статус системы -/sessions - Активные сессии -/logs - Просмотр логов - -# Кластерные команды (только для контроллера) -/cluster status - Статус кластера -/cluster add - Добавить сервер -/cluster logs - Логи агентов -``` - -#========================================================================== -# 🔧 Устранение неполадок -#========================================================================== - -## Проблема: Telegram бот не отвечает -```bash -# Проверить токен бота -grep bot_token /opt/pyguardian/config/config.yaml - -# Проверить подключение к Telegram API -curl -s "https://api.telegram.org/bot/getMe" - -# Проверить логи службы -sudo journalctl -u pyguardian | grep -i telegram -``` - -## Проблема: Агент не подключается к контроллеру -```bash -# На агенте проверить конфигурацию -grep controller_host /opt/pyguardian/config/config.yaml - -# Проверить сетевое подключение -telnet CONTROLLER_IP 8443 - -# Проверить логи агента -sudo journalctl -u pyguardian-agent | grep -i connection -``` - -## Проблема: Высокое использование ресурсов -```bash -# Проверить процессы PyGuardian -ps aux | grep python | grep pyguardian - -# Проверить размер базы данных -du -sh /opt/pyguardian/data/ - -# Оптимизировать базу данных -sqlite3 /opt/pyguardian/data/pyguardian.db "VACUUM;" -``` - -## Проблема: Ошибки файрвола -```bash -# Проверить правила iptables -sudo iptables -L -n - -# Проверить логи файрвола -sudo tail -f /var/log/kern.log | grep -i iptables - -# Временно отключить файрвол PyGuardian -sudo iptables -F PYGUARDIAN 2>/dev/null || true -``` - -#========================================================================== -# 📚 Дополнительные ресурсы -#========================================================================== - -## Документация -- `README.md` - Общее описание проекта -- `docs/INSTALLATION.md` - Подробное руководство по установке -- `docs/CLUSTER_SETUP.md` - Настройка кластера -- `examples/configurations.md` - Примеры конфигураций -- `examples/telegram-commands.md` - Команды Telegram бота - -## Полезные команды -```bash -# Проверить версию PyGuardian -/opt/pyguardian/venv/bin/python main.py --version - -# Создать резервную копию -sudo tar -czf pyguardian-backup-$(date +%Y%m%d).tar.gz \ - /opt/pyguardian/config \ - /opt/pyguardian/data - -# Обновить систему -cd /opt/pyguardian -sudo git pull origin main -sudo systemctl restart pyguardian - -# Полная переустановка -sudo ./install.sh --reinstall -``` - -## Мониторинг и метрики -```bash -# Статистика файрвола -sudo iptables -L -v -n - -# Использование ресурсов -htop -df -h -free -h - -# Сетевые соединения -sudo netstat -tulpn | grep python - -# Логи в реальном времени -sudo tail -f /opt/pyguardian/logs/pyguardian.log -``` - -#========================================================================== -# 🎯 Чек-лист после установки -#========================================================================== - -## ✅ Проверить после установки автономного режима: -- [ ] Служба PyGuardian запущена и активна -- [ ] Telegram бот отвечает на команды -- [ ] Конфигурация корректна и загружена -- [ ] База данных создана и доступна -- [ ] Файрвол настроен и работает -- [ ] Мониторинг ресурсов активен -- [ ] Логи пишутся корректно - -## ✅ Проверить после установки кластера: -- [ ] Контроллер запущен и доступен -- [ ] API кластера отвечает на запросы -- [ ] SSH ключи настроены для развертывания -- [ ] Агенты подключены к контроллеру -- [ ] Кластерные команды работают в Telegram -- [ ] Синхронизация конфигураций работает -- [ ] Мониторинг всех узлов активен - -## ✅ Проверить после Docker установки: -- [ ] Контейнеры запущены и работают -- [ ] Volumes примонтированы корректно -- [ ] Привилегированный режим работает -- [ ] Сеть host доступна -- [ ] Логи контейнеров доступны -- [ ] Автоперезапуск настроен - -#========================================================================== -# 🆘 Получение поддержки -#========================================================================== - -## Сбор диагностической информации -```bash -# Создать диагностический отчет -sudo /opt/pyguardian/scripts/diagnostic-report.sh - -# Отправить логи разработчикам -# В Telegram боте: /debug export -``` - -## Контакты для поддержки -- 📧 Email: support@smartsoltech.com -- 💬 Telegram: @PyGuardianSupport -- 🐛 Issues: GitHub Issues -- 📖 Wiki: GitHub Wiki - -## Перед обращением в поддержку: -1. Запустить тест установки: `./scripts/test-install.sh` -2. Собрать диагностическую информацию -3. Описать проблему и шаги для воспроизведения -4. Приложить релевантные логи и конфигурации - ---- - -**🎉 Поздравляем! PyGuardian готов к работе!** - -Ваша система безопасности настроена и готова защищать серверы. -Не забудьте настроить регулярные резервные копии и мониторинг обновлений. - -*Happy securing! 🛡️* \ No newline at end of file diff --git a/.history/README_20251125195337.md b/.history/README_20251125195337.md deleted file mode 100644 index 17b2ecc..0000000 --- a/.history/README_20251125195337.md +++ /dev/null @@ -1,458 +0,0 @@ -# PyGuardian - Linux Server Protection System - -🛡️ **Полноценная система мониторинга и защиты Linux-сервера от брутфорс-атак с управлением через Telegram-бота** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🎯 Возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125201051.md b/.history/README_20251125201051.md deleted file mode 100644 index f765d92..0000000 --- a/.history/README_20251125201051.md +++ /dev/null @@ -1,475 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125201114.md b/.history/README_20251125201114.md deleted file mode 100644 index f087c9e..0000000 --- a/.history/README_20251125201114.md +++ /dev/null @@ -1,491 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202055.md b/.history/README_20251125202055.md deleted file mode 100644 index f087c9e..0000000 --- a/.history/README_20251125202055.md +++ /dev/null @@ -1,491 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202758.md b/.history/README_20251125202758.md deleted file mode 100644 index 137ec76..0000000 --- a/.history/README_20251125202758.md +++ /dev/null @@ -1,494 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202821.md b/.history/README_20251125202821.md deleted file mode 100644 index 2641506..0000000 --- a/.history/README_20251125202821.md +++ /dev/null @@ -1,504 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202829.md b/.history/README_20251125202829.md deleted file mode 100644 index 97edbec..0000000 --- a/.history/README_20251125202829.md +++ /dev/null @@ -1,512 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202843.md b/.history/README_20251125202843.md deleted file mode 100644 index 788dcb7..0000000 --- a/.history/README_20251125202843.md +++ /dev/null @@ -1,524 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202900.md b/.history/README_20251125202900.md deleted file mode 100644 index bed93bd..0000000 --- a/.history/README_20251125202900.md +++ /dev/null @@ -1,530 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -### Кластерное управление -- **Centralized Control**: Управление множественными серверами из единого центра -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Distributed Monitoring**: Мониторинг безопасности всех серверов кластера -- **Unified Dashboard**: Единый Telegram-интерфейс для всего кластера - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [ ] Кластерный режим -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202914.md b/.history/README_20251125202914.md deleted file mode 100644 index 82fd6da..0000000 --- a/.history/README_20251125202914.md +++ /dev/null @@ -1,530 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -### Кластерное управление -- **Centralized Control**: Управление множественными серверами из единого центра -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Distributed Monitoring**: Мониторинг безопасности всех серверов кластера -- **Unified Dashboard**: Единый Telegram-интерфейс для всего кластера - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125202931.md b/.history/README_20251125202931.md deleted file mode 100644 index 01c3cd4..0000000 --- a/.history/README_20251125202931.md +++ /dev/null @@ -1,577 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -### Кластерное управление -- **Centralized Control**: Управление множественными серверами из единого центра -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Distributed Monitoring**: Мониторинг безопасности всех серверов кластера -- **Unified Dashboard**: Единый Telegram-интерфейс для всего кластера - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125203709.md b/.history/README_20251125203709.md deleted file mode 100644 index 01c3cd4..0000000 --- a/.history/README_20251125203709.md +++ /dev/null @@ -1,577 +0,0 @@ -# PyGuardian - Advanced Linux Server Protection System - -🛡️ **Система мониторинга и защиты Linux-сервера от брутфорс-атак с функциями СКРЫТОГО обнаружения взломов** - -[![Python](https://img.shields.io/badge/Python-3.10+-blue.svg)](https://python.org) -[![License](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE) -[![Telegram](https://img.shields.io/badge/Telegram-Bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -### Кластерное управление -- **Centralized Control**: Управление множественными серверами из единого центра -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Distributed Monitoring**: Мониторинг безопасности всех серверов кластера -- **Unified Dashboard**: Единый Telegram-интерфейс для всего кластера - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125204516.md b/.history/README_20251125204516.md deleted file mode 100644 index 5c22848..0000000 --- a/.history/README_20251125204516.md +++ /dev/null @@ -1,578 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## 🚨 НОВЫЕ ВОЗМОЖНОСТИ - STEALTH SECURITY - -### Скрытое обнаружение компромиссов -- **Invisible Detection**: Система работает **незаметно** для атакующего -- **Honeypot Users**: Автоматическое обнаружение входов под приманочными учетками -- **Behavioral Analysis**: Анализ подозрительного поведения после входа -- **Silent Response**: Тихая блокировка без уведомления атакующего - -### Автоматические контрмеры -- **Password Rotation**: Мгновенная смена паролей скомпрометированных аккаунтов -- **Session Termination**: Принудительное завершение подозрительных сессий -- **Stealth Blocking**: Скрытая блокировка атакующих IP -- **Evidence Collection**: Сбор доказательств для анализа - -### Кластерное управление -- **Centralized Control**: Управление множественными серверами из единого центра -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Distributed Monitoring**: Мониторинг безопасности всех серверов кластера -- **Unified Dashboard**: Единый Telegram-интерфейс для всего кластера - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125204558.md b/.history/README_20251125204558.md deleted file mode 100644 index 62f074b..0000000 --- a/.history/README_20251125204558.md +++ /dev/null @@ -1,595 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## ⚡ Быстрый старт - -### 🚀 Автоматическая установка (рекомендуется) -```bash -# Скачать и запустить установку -wget https://raw.githubusercontent.com/your-repo/PyGuardian/main/install.sh -chmod +x install.sh -sudo ./install.sh -``` - -### 📦 Установка из исходного кода -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему перед установкой -./scripts/test-install.sh - -# Установить (автономный режим) -sudo make install - -# Или установить кластерный контроллер -sudo make controller -``` - -### 🐳 Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Или через Docker Compose -docker-compose up -d -``` - -**📖 Подробные инструкции:** [QUICKSTART.md](QUICKSTART.md) - -## 🎯 Основные возможности - -- **Мониторинг в реальном времени**: Отслеживание auth.log и детекция атак -- **Автоматическая защита**: Блокировка IP при превышении лимита попыток -- **Telegram управление**: Полный контроль через бота без веб-интерфейса -- **🔥 STEALTH MODE**: Скрытое обнаружение и нейтрализация взломов -- **Session Management**: Мониторинг и управление SSH сессиями -- **Password Security**: Автоматическая смена паролей при компромиссе -- **🌐 CLUSTER MANAGEMENT**: Централизованное управление множественными серверами -- **Agent Deployment**: Автоматическое развертывание агентов по SSH -- **Multi-Server Control**: Единый Telegram-интерфейс для всех серверов -- **Поддержка firewall**: iptables и nftables -- **Автоматический разбан**: По таймеру с уведомлениями -- **Белый список**: Защита доверенных IP от блокировки -- **Статистика**: Подробная аналитика атак и успешных входов -- **Асинхронность**: Оптимизированная производительность - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125204623.md b/.history/README_20251125204623.md deleted file mode 100644 index 1c07dfe..0000000 --- a/.history/README_20251125204623.md +++ /dev/null @@ -1,609 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## ⚡ Быстрый старт - -### 🚀 Автоматическая установка (рекомендуется) -```bash -# Скачать и запустить установку -wget https://raw.githubusercontent.com/your-repo/PyGuardian/main/install.sh -chmod +x install.sh -sudo ./install.sh -``` - -### 📦 Установка из исходного кода -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему перед установкой -./scripts/test-install.sh - -# Установить (автономный режим) -sudo make install - -# Или установить кластерный контроллер -sudo make controller -``` - -### 🐳 Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Или через Docker Compose -docker-compose up -d -``` - -**📖 Подробные инструкции:** [QUICKSTART.md](QUICKSTART.md) - -## 🎯 Основные возможности - -### 🔒 Продвинутая система безопасности -- **🚨 Обнаружение вторжений** - Real-time детекция атак -- **🛡️ Интеллектуальный файрвол** - Автоматическая блокировка угроз -- **🔍 Мониторинг системы** - Контроль ресурсов и процессов -- **⚠️ DDoS защита** - Автоматическое обнаружение и блокировка -- **🔐 2FA интеграция** - TOTP аутентификация через Telegram -- **👤 Stealth Mode** - Скрытое обнаружение компрометации - -### 🌐 Кластерное управление -- **🎛️ Централизованный контроль** - Управление множеством серверов -- **🚀 Автоматическое развертывание** - Развертывание агентов одной командой -- **📡 Синхронизация конфигураций** - Единые политики безопасности -- **📊 Мониторинг кластера** - Состояние всех узлов в реальном времени -- **🔄 Load Balancing** - Автоматическое распределение нагрузки - -### 💬 Продвинутый Telegram интерфейс -- **🤖 Интерактивные команды** - Удобное управление через диалоги -- **📈 Real-time мониторинг** - Мгновенные уведомления и алерты -- **🔧 Удаленное управление** - Полный контроль через мессенджер -- **👥 Многопользовательский доступ** - Ролевая модель доступа -- **🗣️ Поддержка голосовых команд** - Управление голосом - -### 🐳 Современные технологии развертывания -- **📦 Docker поддержка** - Контейнеризированное развертывание -- **⚙️ systemd интеграция** - Нативная интеграция с системой -- **🔧 Ansible ready** - Готовые playbooks для автоматизации -- **☁️ Cloud готовность** - Поддержка AWS, GCP, Azure -- **📊 Метрики и логирование** - Интеграция с Grafana/Prometheus - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125204704.md b/.history/README_20251125204704.md deleted file mode 100644 index 1c07dfe..0000000 --- a/.history/README_20251125204704.md +++ /dev/null @@ -1,609 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## ⚡ Быстрый старт - -### 🚀 Автоматическая установка (рекомендуется) -```bash -# Скачать и запустить установку -wget https://raw.githubusercontent.com/your-repo/PyGuardian/main/install.sh -chmod +x install.sh -sudo ./install.sh -``` - -### 📦 Установка из исходного кода -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему перед установкой -./scripts/test-install.sh - -# Установить (автономный режим) -sudo make install - -# Или установить кластерный контроллер -sudo make controller -``` - -### 🐳 Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Или через Docker Compose -docker-compose up -d -``` - -**📖 Подробные инструкции:** [QUICKSTART.md](QUICKSTART.md) - -## 🎯 Основные возможности - -### 🔒 Продвинутая система безопасности -- **🚨 Обнаружение вторжений** - Real-time детекция атак -- **🛡️ Интеллектуальный файрвол** - Автоматическая блокировка угроз -- **🔍 Мониторинг системы** - Контроль ресурсов и процессов -- **⚠️ DDoS защита** - Автоматическое обнаружение и блокировка -- **🔐 2FA интеграция** - TOTP аутентификация через Telegram -- **👤 Stealth Mode** - Скрытое обнаружение компрометации - -### 🌐 Кластерное управление -- **🎛️ Централизованный контроль** - Управление множеством серверов -- **🚀 Автоматическое развертывание** - Развертывание агентов одной командой -- **📡 Синхронизация конфигураций** - Единые политики безопасности -- **📊 Мониторинг кластера** - Состояние всех узлов в реальном времени -- **🔄 Load Balancing** - Автоматическое распределение нагрузки - -### 💬 Продвинутый Telegram интерфейс -- **🤖 Интерактивные команды** - Удобное управление через диалоги -- **📈 Real-time мониторинг** - Мгновенные уведомления и алерты -- **🔧 Удаленное управление** - Полный контроль через мессенджер -- **👥 Многопользовательский доступ** - Ролевая модель доступа -- **🗣️ Поддержка голосовых команд** - Управление голосом - -### 🐳 Современные технологии развертывания -- **📦 Docker поддержка** - Контейнеризированное развертывание -- **⚙️ systemd интеграция** - Нативная интеграция с системой -- **🔧 Ansible ready** - Готовые playbooks для автоматизации -- **☁️ Cloud готовность** - Поддержка AWS, GCP, Azure -- **📊 Метрики и логирование** - Интеграция с Grafana/Prometheus - -## 📋 Требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **Права**: root (для работы с firewall) -- **Firewall**: iptables или nftables -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125204708.md b/.history/README_20251125204708.md deleted file mode 100644 index abc624e..0000000 --- a/.history/README_20251125204708.md +++ /dev/null @@ -1,822 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## ⚡ Быстрый старт - -### 🚀 Автоматическая установка (рекомендуется) -```bash -# Скачать и запустить установку -wget https://raw.githubusercontent.com/your-repo/PyGuardian/main/install.sh -chmod +x install.sh -sudo ./install.sh -``` - -### 📦 Установка из исходного кода -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему перед установкой -./scripts/test-install.sh - -# Установить (автономный режим) -sudo make install - -# Или установить кластерный контроллер -sudo make controller -``` - -### 🐳 Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Или через Docker Compose -docker-compose up -d -``` - -**📖 Подробные инструкции:** [QUICKSTART.md](QUICKSTART.md) - -## 🎯 Основные возможности - -### 🔒 Продвинутая система безопасности -- **🚨 Обнаружение вторжений** - Real-time детекция атак -- **🛡️ Интеллектуальный файрвол** - Автоматическая блокировка угроз -- **🔍 Мониторинг системы** - Контроль ресурсов и процессов -- **⚠️ DDoS защита** - Автоматическое обнаружение и блокировка -- **🔐 2FA интеграция** - TOTP аутентификация через Telegram -- **👤 Stealth Mode** - Скрытое обнаружение компрометации - -### 🌐 Кластерное управление -- **🎛️ Централизованный контроль** - Управление множеством серверов -- **🚀 Автоматическое развертывание** - Развертывание агентов одной командой -- **📡 Синхронизация конфигураций** - Единые политики безопасности -- **📊 Мониторинг кластера** - Состояние всех узлов в реальном времени -- **🔄 Load Balancing** - Автоматическое распределение нагрузки - -### 💬 Продвинутый Telegram интерфейс -- **🤖 Интерактивные команды** - Удобное управление через диалоги -- **📈 Real-time мониторинг** - Мгновенные уведомления и алерты -- **🔧 Удаленное управление** - Полный контроль через мессенджер -- **👥 Многопользовательский доступ** - Ролевая модель доступа -- **🗣️ Поддержка голосовых команд** - Управление голосом - -### 🐳 Современные технологии развертывания -- **📦 Docker поддержка** - Контейнеризированное развертывание -- **⚙️ systemd интеграция** - Нативная интеграция с системой -- **🔧 Ansible ready** - Готовые playbooks для автоматизации -- **☁️ Cloud готовность** - Поддержка AWS, GCP, Azure -- **📊 Метрики и логирование** - Интеграция с Grafana/Prometheus - -## 🏗️ Архитектура системы - -### Режимы развертывания: - -#### 🖥️ Standalone (Автономный) -Все компоненты на одном сервере -``` -┌─────────────────┐ -│ PyGuardian │ -│ ┌─────────────┐ │ -│ │ Telegram │ │ -│ │ Bot │ │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Security │ │ -│ │ Monitor │ │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Firewall │ │ -│ │ Manager │ │ -│ └─────────────┘ │ -└─────────────────┘ -``` - -#### 🌐 Controller + Agents (Кластерный) -Центральный контроллер управляет агентами -``` -┌─────────────────┐ ┌─────────────────┐ -│ Controller │────│ Agent 1 │ -│ ┌─────────────┐ │ │ ┌─────────────┐ │ -│ │ Telegram │ │ │ │ Security │ │ -│ │ Bot │ │ │ │ Monitor │ │ -│ └─────────────┘ │ │ └─────────────┘ │ -│ ┌─────────────┐ │ └─────────────────┘ -│ │ Cluster │ │ │ -│ │ Manager │ │ ┌─────────────────┐ -│ └─────────────┘ │────│ Agent 2 │ -└─────────────────┘ │ ┌─────────────┐ │ - │ │ Security │ │ - │ │ Monitor │ │ - │ └─────────────┘ │ - └─────────────────┘ -``` - -## 🛠️ Технологический стек - -- **🐍 Python 3.10+** - Основной язык разработки -- **🤖 Telegram Bot API** - Интерфейс управления -- **🗄️ SQLite/PostgreSQL** - База данных -- **🔥 iptables/nftables** - Управление файрволом -- **🐳 Docker** - Контейнеризация -- **⚙️ systemd** - Управление службами -- **🔒 cryptography** - Шифрование данных -- **📡 asyncio** - Асинхронное выполнение -- **📊 psutil** - Мониторинг системы - -## 📋 Требования - -### Минимальные требования: -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **RAM**: 512MB (автономный), 1GB (контроллер) -- **Диск**: 1GB свободного места -- **Сеть**: Доступ к Telegram API - -### Рекомендуемые требования: -- **ОС**: Ubuntu 22.04 LTS / Debian 12 -- **Python**: 3.11+ -- **RAM**: 2GB+ для контроллера кластера -- **Диск**: 10GB+ для логов и резервных копий -- **Сеть**: Выделенный IP для кластера - -## 📚 Документация - -- **[📖 QUICKSTART.md](QUICKSTART.md)** - Быстрое руководство по установке -- **[⚙️ INSTALLATION.md](docs/INSTALLATION.md)** - Подробная установка и настройка -- **[🌐 CLUSTER_SETUP.md](docs/CLUSTER_SETUP.md)** - Настройка кластера -- **[⚡ configurations.md](examples/configurations.md)** - Примеры конфигураций -- **[🤖 telegram-commands.md](examples/telegram-commands.md)** - Команды бота - -## 🔧 Установка и использование - -### 1️⃣ Автоматическая установка -```bash -# Автономная установка -sudo ./install.sh - -# Контроллер кластера -sudo ./install.sh --mode controller - -# Агент кластера -sudo ./install.sh --mode agent --controller 192.168.1.10 -``` - -### 2️⃣ Make файл -```bash -# Показать все доступные команды -make help - -# Установка различных режимов -make install # Автономный режим -make controller # Кластерный контроллер -make agent # Кластерный агент -``` - -### 3️⃣ Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Ручная сборка образа -docker build -t pyguardian . -docker run -d --privileged --network host pyguardian -``` - -## 🎯 Примеры использования - -### Управление через Telegram бота: -``` -/start - Начать работу с ботом -/status - Статус системы безопасности -/block IP - Заблокировать IP адрес -/unblock IP - Разблокировать IP адрес -/sessions - Показать активные сессии -/logs - Просмотр логов -/cluster status - Статус кластера (контроллер) -/cluster add - Добавить сервер в кластер -``` - -### Кластерное управление: -```bash -# Добавление сервера в кластер через SSH -ssh-copy-id -i ~/.ssh/cluster_key.pub root@192.168.1.50 - -# В Telegram боте контроллера: -/cluster add -# Следовать интерактивным инструкциям -``` - -## 🔒 Безопасность - -- **🔑 Аутентификация**: Telegram user ID + опциональная 2FA -- **🔐 Шифрование**: Все конфиденциальные данные зашифрованы -- **🛡️ Изоляция**: Контейнеризация и изолированные процессы -- **📝 Аудит**: Полное логирование всех действий -- **🚫 Принцип минимальных привилегий**: Только необходимые права - -## 📊 Мониторинг и алерты - -### Автоматические уведомления о: -- 🚨 Попытках взлома и атаках -- 📈 Превышении лимитов ресурсов -- 🔌 Подключении/отключении агентов кластера -- ⚠️ Ошибках в системе безопасности -- 📋 Результатах резервного копирования - -### Интеграция с системами мониторинга: -- **Grafana/Prometheus** - Метрики и дашборды -- **ELK Stack** - Централизованное логирование -- **SIEM системы** - Экспорт событий безопасности - -## 🤝 Вклад в проект - -Мы приветствуем вклад в развитие PyGuardian! - -### Как принять участие: -1. **Fork** репозитория -2. Создайте **feature branch** (`git checkout -b feature/amazing-feature`) -3. **Commit** изменения (`git commit -m 'Add amazing feature'`) -4. **Push** в branch (`git push origin feature/amazing-feature`) -5. Создайте **Pull Request** - -### Области для улучшения: -- 🌐 Веб-интерфейс управления -- 📱 Мобильное приложение -- 🔌 Интеграции с облачными провайдерами -- 🤖 ИИ для детекции аномалий -- 📊 Расширенная аналитика - -## 📄 Лицензия - -Этот проект распространяется под лицензией MIT. См. файл [LICENSE](LICENSE) для подробностей. - -## 👥 Авторы - -- **SmartSolTech Team** - *Первоначальная разработка* - [@SmartSolTech](https://github.com/SmartSolTech) - -## 🆘 Поддержка - -### Получить помощь: -- 📧 **Email**: support@smartsoltech.com -- 💬 **Telegram**: @PyGuardianSupport -- 🐛 **Issues**: [GitHub Issues](https://github.com/SmartSolTech/PyGuardian/issues) -- 📖 **Wiki**: [GitHub Wiki](https://github.com/SmartSolTech/PyGuardian/wiki) - -### Перед обращением: -1. Проверьте [FAQ](https://github.com/SmartSolTech/PyGuardian/wiki/FAQ) -2. Запустите диагностику: `./scripts/test-install.sh` -3. Соберите логи: `/debug export` в Telegram боте - ---- - -**⚡ Быстрые команды:** - -```bash -# Проверить статус -systemctl status pyguardian - -# Просмотреть логи -journalctl -u pyguardian -f - -# Перезапустить -systemctl restart pyguardian - -# Обновить конфигурацию -systemctl reload pyguardian -``` - -**🎉 Спасибо за использование PyGuardian! Ваша безопасность - наш приоритет. 🛡️** -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125204848.md b/.history/README_20251125204848.md deleted file mode 100644 index abc624e..0000000 --- a/.history/README_20251125204848.md +++ /dev/null @@ -1,822 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## ⚡ Быстрый старт - -### 🚀 Автоматическая установка (рекомендуется) -```bash -# Скачать и запустить установку -wget https://raw.githubusercontent.com/your-repo/PyGuardian/main/install.sh -chmod +x install.sh -sudo ./install.sh -``` - -### 📦 Установка из исходного кода -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему перед установкой -./scripts/test-install.sh - -# Установить (автономный режим) -sudo make install - -# Или установить кластерный контроллер -sudo make controller -``` - -### 🐳 Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Или через Docker Compose -docker-compose up -d -``` - -**📖 Подробные инструкции:** [QUICKSTART.md](QUICKSTART.md) - -## 🎯 Основные возможности - -### 🔒 Продвинутая система безопасности -- **🚨 Обнаружение вторжений** - Real-time детекция атак -- **🛡️ Интеллектуальный файрвол** - Автоматическая блокировка угроз -- **🔍 Мониторинг системы** - Контроль ресурсов и процессов -- **⚠️ DDoS защита** - Автоматическое обнаружение и блокировка -- **🔐 2FA интеграция** - TOTP аутентификация через Telegram -- **👤 Stealth Mode** - Скрытое обнаружение компрометации - -### 🌐 Кластерное управление -- **🎛️ Централизованный контроль** - Управление множеством серверов -- **🚀 Автоматическое развертывание** - Развертывание агентов одной командой -- **📡 Синхронизация конфигураций** - Единые политики безопасности -- **📊 Мониторинг кластера** - Состояние всех узлов в реальном времени -- **🔄 Load Balancing** - Автоматическое распределение нагрузки - -### 💬 Продвинутый Telegram интерфейс -- **🤖 Интерактивные команды** - Удобное управление через диалоги -- **📈 Real-time мониторинг** - Мгновенные уведомления и алерты -- **🔧 Удаленное управление** - Полный контроль через мессенджер -- **👥 Многопользовательский доступ** - Ролевая модель доступа -- **🗣️ Поддержка голосовых команд** - Управление голосом - -### 🐳 Современные технологии развертывания -- **📦 Docker поддержка** - Контейнеризированное развертывание -- **⚙️ systemd интеграция** - Нативная интеграция с системой -- **🔧 Ansible ready** - Готовые playbooks для автоматизации -- **☁️ Cloud готовность** - Поддержка AWS, GCP, Azure -- **📊 Метрики и логирование** - Интеграция с Grafana/Prometheus - -## 🏗️ Архитектура системы - -### Режимы развертывания: - -#### 🖥️ Standalone (Автономный) -Все компоненты на одном сервере -``` -┌─────────────────┐ -│ PyGuardian │ -│ ┌─────────────┐ │ -│ │ Telegram │ │ -│ │ Bot │ │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Security │ │ -│ │ Monitor │ │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Firewall │ │ -│ │ Manager │ │ -│ └─────────────┘ │ -└─────────────────┘ -``` - -#### 🌐 Controller + Agents (Кластерный) -Центральный контроллер управляет агентами -``` -┌─────────────────┐ ┌─────────────────┐ -│ Controller │────│ Agent 1 │ -│ ┌─────────────┐ │ │ ┌─────────────┐ │ -│ │ Telegram │ │ │ │ Security │ │ -│ │ Bot │ │ │ │ Monitor │ │ -│ └─────────────┘ │ │ └─────────────┘ │ -│ ┌─────────────┐ │ └─────────────────┘ -│ │ Cluster │ │ │ -│ │ Manager │ │ ┌─────────────────┐ -│ └─────────────┘ │────│ Agent 2 │ -└─────────────────┘ │ ┌─────────────┐ │ - │ │ Security │ │ - │ │ Monitor │ │ - │ └─────────────┘ │ - └─────────────────┘ -``` - -## 🛠️ Технологический стек - -- **🐍 Python 3.10+** - Основной язык разработки -- **🤖 Telegram Bot API** - Интерфейс управления -- **🗄️ SQLite/PostgreSQL** - База данных -- **🔥 iptables/nftables** - Управление файрволом -- **🐳 Docker** - Контейнеризация -- **⚙️ systemd** - Управление службами -- **🔒 cryptography** - Шифрование данных -- **📡 asyncio** - Асинхронное выполнение -- **📊 psutil** - Мониторинг системы - -## 📋 Требования - -### Минимальные требования: -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **RAM**: 512MB (автономный), 1GB (контроллер) -- **Диск**: 1GB свободного места -- **Сеть**: Доступ к Telegram API - -### Рекомендуемые требования: -- **ОС**: Ubuntu 22.04 LTS / Debian 12 -- **Python**: 3.11+ -- **RAM**: 2GB+ для контроллера кластера -- **Диск**: 10GB+ для логов и резервных копий -- **Сеть**: Выделенный IP для кластера - -## 📚 Документация - -- **[📖 QUICKSTART.md](QUICKSTART.md)** - Быстрое руководство по установке -- **[⚙️ INSTALLATION.md](docs/INSTALLATION.md)** - Подробная установка и настройка -- **[🌐 CLUSTER_SETUP.md](docs/CLUSTER_SETUP.md)** - Настройка кластера -- **[⚡ configurations.md](examples/configurations.md)** - Примеры конфигураций -- **[🤖 telegram-commands.md](examples/telegram-commands.md)** - Команды бота - -## 🔧 Установка и использование - -### 1️⃣ Автоматическая установка -```bash -# Автономная установка -sudo ./install.sh - -# Контроллер кластера -sudo ./install.sh --mode controller - -# Агент кластера -sudo ./install.sh --mode agent --controller 192.168.1.10 -``` - -### 2️⃣ Make файл -```bash -# Показать все доступные команды -make help - -# Установка различных режимов -make install # Автономный режим -make controller # Кластерный контроллер -make agent # Кластерный агент -``` - -### 3️⃣ Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Ручная сборка образа -docker build -t pyguardian . -docker run -d --privileged --network host pyguardian -``` - -## 🎯 Примеры использования - -### Управление через Telegram бота: -``` -/start - Начать работу с ботом -/status - Статус системы безопасности -/block IP - Заблокировать IP адрес -/unblock IP - Разблокировать IP адрес -/sessions - Показать активные сессии -/logs - Просмотр логов -/cluster status - Статус кластера (контроллер) -/cluster add - Добавить сервер в кластер -``` - -### Кластерное управление: -```bash -# Добавление сервера в кластер через SSH -ssh-copy-id -i ~/.ssh/cluster_key.pub root@192.168.1.50 - -# В Telegram боте контроллера: -/cluster add -# Следовать интерактивным инструкциям -``` - -## 🔒 Безопасность - -- **🔑 Аутентификация**: Telegram user ID + опциональная 2FA -- **🔐 Шифрование**: Все конфиденциальные данные зашифрованы -- **🛡️ Изоляция**: Контейнеризация и изолированные процессы -- **📝 Аудит**: Полное логирование всех действий -- **🚫 Принцип минимальных привилегий**: Только необходимые права - -## 📊 Мониторинг и алерты - -### Автоматические уведомления о: -- 🚨 Попытках взлома и атаках -- 📈 Превышении лимитов ресурсов -- 🔌 Подключении/отключении агентов кластера -- ⚠️ Ошибках в системе безопасности -- 📋 Результатах резервного копирования - -### Интеграция с системами мониторинга: -- **Grafana/Prometheus** - Метрики и дашборды -- **ELK Stack** - Централизованное логирование -- **SIEM системы** - Экспорт событий безопасности - -## 🤝 Вклад в проект - -Мы приветствуем вклад в развитие PyGuardian! - -### Как принять участие: -1. **Fork** репозитория -2. Создайте **feature branch** (`git checkout -b feature/amazing-feature`) -3. **Commit** изменения (`git commit -m 'Add amazing feature'`) -4. **Push** в branch (`git push origin feature/amazing-feature`) -5. Создайте **Pull Request** - -### Области для улучшения: -- 🌐 Веб-интерфейс управления -- 📱 Мобильное приложение -- 🔌 Интеграции с облачными провайдерами -- 🤖 ИИ для детекции аномалий -- 📊 Расширенная аналитика - -## 📄 Лицензия - -Этот проект распространяется под лицензией MIT. См. файл [LICENSE](LICENSE) для подробностей. - -## 👥 Авторы - -- **SmartSolTech Team** - *Первоначальная разработка* - [@SmartSolTech](https://github.com/SmartSolTech) - -## 🆘 Поддержка - -### Получить помощь: -- 📧 **Email**: support@smartsoltech.com -- 💬 **Telegram**: @PyGuardianSupport -- 🐛 **Issues**: [GitHub Issues](https://github.com/SmartSolTech/PyGuardian/issues) -- 📖 **Wiki**: [GitHub Wiki](https://github.com/SmartSolTech/PyGuardian/wiki) - -### Перед обращением: -1. Проверьте [FAQ](https://github.com/SmartSolTech/PyGuardian/wiki/FAQ) -2. Запустите диагностику: `./scripts/test-install.sh` -3. Соберите логи: `/debug export` в Telegram боте - ---- - -**⚡ Быстрые команды:** - -```bash -# Проверить статус -systemctl status pyguardian - -# Просмотреть логи -journalctl -u pyguardian -f - -# Перезапустить -systemctl restart pyguardian - -# Обновить конфигурацию -systemctl reload pyguardian -``` - -**🎉 Спасибо за использование PyGuardian! Ваша безопасность - наш приоритет. 🛡️** -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/README_20251125205220.md b/.history/README_20251125205220.md deleted file mode 100644 index d8cc8a1..0000000 --- a/.history/README_20251125205220.md +++ /dev/null @@ -1,822 +0,0 @@ -# PyGuardian - Advanced Security & Cluster Management System 🛡️ - -**Комплексная система безопасности с централизованным управлением кластером серверов через Telegram бот.** - -[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Docker](https://img.shields.io/badge/docker-supported-blue.svg)](https://www.docker.com/) -[![Telegram Bot](https://img.shields.io/badge/telegram-bot-blue.svg)](https://core.telegram.org/bots) - -## ⚡ Быстрый старт - -### 🚀 Автоматическая установка (рекомендуется) -```bash -# Скачать и запустить установку -wget https://raw.githubusercontent.com/your-repo/PyGuardian/main/install.sh -chmod +x install.sh -sudo ./install.sh -``` - -### 📦 Установка из исходного кода -```bash -# Клонировать репозиторий -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Проверить систему перед установкой -./scripts/test-install.sh - -# Установить (автономный режим) -sudo make install - -# Или установить кластерный контроллер -sudo make controller -``` - -### 🐳 Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Или через Docker Compose -docker-compose up -d -``` - -**📖 Подробные инструкции:** [QUICKSTART.md](QUICKSTART.md) - -## 🎯 Основные возможности - -### 🔒 Продвинутая система безопасности -- **🚨 Обнаружение вторжений** - Real-time детекция атак -- **🛡️ Интеллектуальный файрвол** - Автоматическая блокировка угроз -- **🔍 Мониторинг системы** - Контроль ресурсов и процессов -- **⚠️ DDoS защита** - Автоматическое обнаружение и блокировка -- **🔐 2FA интеграция** - TOTP аутентификация через Telegram -- **👤 Stealth Mode** - Скрытое обнаружение компрометации - -### 🌐 Кластерное управление -- **🎛️ Централизованный контроль** - Управление множеством серверов -- **🚀 Автоматическое развертывание** - Развертывание агентов одной командой -- **📡 Синхронизация конфигураций** - Единые политики безопасности -- **📊 Мониторинг кластера** - Состояние всех узлов в реальном времени -- **🔄 Load Balancing** - Автоматическое распределение нагрузки - -### 💬 Продвинутый Telegram интерфейс -- **🤖 Интерактивные команды** - Удобное управление через диалоги -- **📈 Real-time мониторинг** - Мгновенные уведомления и алерты -- **🔧 Удаленное управление** - Полный контроль через мессенджер -- **👥 Многопользовательский доступ** - Ролевая модель доступа -- **🗣️ Поддержка голосовых команд** - Управление голосом - -### 🐳 Современные технологии развертывания -- **📦 Docker поддержка** - Контейнеризированное развертывание -- **⚙️ systemd интеграция** - Нативная интеграция с системой -- **🔧 Ansible ready** - Готовые playbooks для автоматизации -- **☁️ Cloud готовность** - Поддержка AWS, GCP, Azure -- **📊 Метрики и логирование** - Интеграция с Grafana/Prometheus - -## 🏗️ Архитектура системы - -### Режимы развертывания: - -#### 🖥️ Standalone (Автономный) -Все компоненты на одном сервере -``` -┌─────────────────┐ -│ PyGuardian │ -│ ┌─────────────┐ │ -│ │ Telegram │ │ -│ │ Bot │ │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Security │ │ -│ │ Monitor │ │ -│ └─────────────┘ │ -│ ┌─────────────┐ │ -│ │ Firewall │ │ -│ │ Manager │ │ -│ └─────────────┘ │ -└─────────────────┘ -``` - -#### 🌐 Controller + Agents (Кластерный) -Центральный контроллер управляет агентами -``` -┌─────────────────┐ ┌─────────────────┐ -│ Controller │────│ Agent 1 │ -│ ┌─────────────┐ │ │ ┌─────────────┐ │ -│ │ Telegram │ │ │ │ Security │ │ -│ │ Bot │ │ │ │ Monitor │ │ -│ └─────────────┘ │ │ └─────────────┘ │ -│ ┌─────────────┐ │ └─────────────────┘ -│ │ Cluster │ │ │ -│ │ Manager │ │ ┌─────────────────┐ -│ └─────────────┘ │────│ Agent 2 │ -└─────────────────┘ │ ┌─────────────┐ │ - │ │ Security │ │ - │ │ Monitor │ │ - │ └─────────────┘ │ - └─────────────────┘ -``` - -## 🛠️ Технологический стек - -- **🐍 Python 3.10+** - Основной язык разработки -- **🤖 Telegram Bot API** - Интерфейс управления -- **🗄️ SQLite/PostgreSQL** - База данных -- **🔥 iptables/nftables** - Управление файрволом -- **🐳 Docker** - Контейнеризация -- **⚙️ systemd** - Управление службами -- **🔒 cryptography** - Шифрование данных -- **📡 asyncio** - Асинхронное выполнение -- **📊 psutil** - Мониторинг системы - -## 📋 Требования - -### Минимальные требования: -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **RAM**: 512MB (автономный), 1GB (контроллер) -- **Диск**: 1GB свободного места -- **Сеть**: Доступ к Telegram API - -### Рекомендуемые требования: -- **ОС**: Ubuntu 22.04 LTS / Debian 12 -- **Python**: 3.11+ -- **RAM**: 2GB+ для контроллера кластера -- **Диск**: 10GB+ для логов и резервных копий -- **Сеть**: Выделенный IP для кластера - -## 📚 Документация - -- **[📖 QUICKSTART.md](QUICKSTART.md)** - Быстрое руководство по установке -- **[⚙️ INSTALLATION.md](docs/INSTALLATION.md)** - Подробная установка и настройка -- **[🌐 CLUSTER_SETUP.md](docs/CLUSTER_SETUP.md)** - Настройка кластера -- **[⚡ configurations.md](examples/configurations.md)** - Примеры конфигураций -- **[🤖 telegram-commands.md](examples/telegram-commands.md)** - Команды бота - -## 🔧 Установка и использование - -### 1️⃣ Автоматическая установка -```bash -# Автономная установка -sudo ./install.sh - -# Контроллер кластера -sudo ./install.sh --mode controller - -# Агент кластера -sudo ./install.sh --mode agent --controller 192.168.1.10 -``` - -### 2️⃣ Make файл -```bash -# Показать все доступные команды -make help - -# Установка различных режимов -make install # Автономный режим -make controller # Кластерный контроллер -make agent # Кластерный агент -``` - -### 3️⃣ Docker установка -```bash -# Автоматическая Docker установка -./scripts/docker-install.sh - -# Ручная сборка образа -docker build -t pyguardian . -docker run -d --privileged --network host pyguardian -``` - -## 🎯 Примеры использования - -### Управление через Telegram бота: -``` -/start - Начать работу с ботом -/status - Статус системы безопасности -/block IP - Заблокировать IP адрес -/unblock IP - Разблокировать IP адрес -/sessions - Показать активные сессии -/logs - Просмотр логов -/cluster status - Статус кластера (контроллер) -/cluster add - Добавить сервер в кластер -``` - -### Кластерное управление: -```bash -# Добавление сервера в кластер через SSH -ssh-copy-id -i ~/.ssh/cluster_key.pub root@192.168.1.50 - -# В Telegram боте контроллера: -/cluster add -# Следовать интерактивным инструкциям -``` - -## 🔒 Безопасность - -- **🔑 Аутентификация**: Telegram user ID + опциональная 2FA -- **🔐 Шифрование**: Все конфиденциальные данные зашифрованы -- **🛡️ Изоляция**: Контейнеризация и изолированные процессы -- **📝 Аудит**: Полное логирование всех действий -- **🚫 Принцип минимальных привилегий**: Только необходимые права - -## 📊 Мониторинг и алерты - -### Автоматические уведомления о: -- 🚨 Попытках взлома и атаках -- 📈 Превышении лимитов ресурсов -- 🔌 Подключении/отключении агентов кластера -- ⚠️ Ошибках в системе безопасности -- 📋 Результатах резервного копирования - -### Интеграция с системами мониторинга: -- **Grafana/Prometheus** - Метрики и дашборды -- **ELK Stack** - Централизованное логирование -- **SIEM системы** - Экспорт событий безопасности - -## 🤝 Вклад в проект - -Мы приветствуем вклад в развитие PyGuardian! - -### Как принять участие: -1. **Fork** репозитория -2. Создайте **feature branch** (`git checkout -b feature/amazing-feature`) -3. **Commit** изменения (`git commit -m 'Add amazing feature'`) -4. **Push** в branch (`git push origin feature/amazing-feature`) -5. Создайте **Pull Request** - -### Области для улучшения: -- 🌐 Веб-интерфейс управления -- 📱 Мобильное приложение -- 🔌 Интеграции с облачными провайдерами -- 🤖 ИИ для детекции аномалий -- 📊 Расширенная аналитика - -## 📄 Лицензия - -Этот проект распространяется под лицензией MIT. См. файл [LICENSE](LICENSE) для подробностей. - -## 👥 Авторы - -- **SmartSolTech Team** - *Первоначальная разработка* - [@SmartSolTech](https://github.com/SmartSolTech) - -## 🆘 Поддержка - -### Получить помощь: -- 📧 **Email**: support@smartsoltech.com -- 💬 **Telegram**: @PyGuardianSupport -- 🐛 **Issues**: [GitHub Issues](https://github.com/SmartSolTech/PyGuardian/issues) -- 📖 **Wiki**: [GitHub Wiki](https://github.com/SmartSolTech/PyGuardian/wiki) - -### Перед обращением: -1. Проверьте [FAQ](https://github.com/SmartSolTech/PyGuardian/wiki/FAQ) -2. Запустите диагностику: `./scripts/test-install.sh` -3. Соберите логи: `/debug export` в Telegram боте - ---- - -**⚡ Быстрые команды:** - -```bash -# Проверить статус -systemctl status pyguardian - -# Просмотреть логи -journalctl -u pyguardian -f - -# Перезапустить -systemctl restart pyguardian - -# Обновить конфигурацию -systemctl reload pyguardian -``` - -**🎉 Спасибо за использование PyGuardian! Ваша безопасность - наш приоритет. 🛡️** -- **Telegram**: Токен бота и admin ID - -## 🚀 Быстрая установка - -### 1. Клонирование репозитория -```bash -git clone https://github.com/your-org/pyguardian.git -cd pyguardian -``` - -### 2. Автоматическая установка -```bash -sudo chmod +x install.sh -sudo ./install.sh -``` - -### 3. Настройка Telegram бота - -#### Создание бота: -1. Отправьте `/newbot` боту [@BotFather](https://t.me/BotFather) -2. Следуйте инструкциям и получите токен -3. Узнайте ваш Telegram ID через [@userinfobot](https://t.me/userinfobot) - -#### Обновление конфигурации: -```bash -sudo nano /etc/pyguardian/config.yaml -``` - -```yaml -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_id: YOUR_TELEGRAM_ID -``` - -### 4. Запуск сервиса -```bash -# Запуск -sudo systemctl start pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Проверка статуса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f -``` - -## ⚙️ Конфигурация - -### Основные параметры - -| Параметр | Описание | По умолчанию | -|----------|----------|--------------| -| `max_attempts` | Максимум попыток за time_window | 5 | -| `time_window` | Окно времени в секундах | 60 | -| `unban_time` | Время автоматической разблокировки | 3600 | -| `auth_log_path` | Путь к auth.log | `/var/log/auth.log` | -| `check_interval` | Интервал проверки лога | 1.0 | - -### Firewall настройки - -#### Для iptables: -```yaml -firewall: - backend: "iptables" - chain: "INPUT" - target: "DROP" - iptables: - table: "filter" -``` - -#### Для nftables: -```yaml -firewall: - backend: "nftables" - nftables: - table: "inet pyguardian" - chain: "input" -``` - -### Белый список IP -```yaml -whitelist: - - "127.0.0.1" - - "::1" - - "192.168.1.0/24" # Локальная сеть - - "10.0.0.0/8" # VPN сеть -``` - -### Конфигурация кластера -```yaml -cluster: - enabled: true - ssh_timeout: 30 - deployment_timeout: 300 - retry_attempts: 3 - agent_port: 8081 - master_host: "192.168.1.100" - master_port: 8080 -``` - -## 🤖 Команды Telegram бота - -### Основные команды -| Команда | Описание | -|---------|----------| -| `/start` | Приветствие и информация о системе | -| `/status` | Статистика атак и система | -| `/top10` | Топ-10 атакующих IP за сутки | -| `/details ` | Детальная информация по IP | -| `/ban ` | Ручная блокировка IP | -| `/unban ` | Разблокировка IP | -| `/list` | Список всех забаненных IP | -| `/help` | Справка по командам | - -### 🚨 Новые команды управления безопасностью -| Команда | Описание | -|---------|----------| -| `/compromises` | Список обнаруженных взломов | -| `/sessions` | Активные SSH сессии | -| `/kick ` | Завершить сессию пользователя | -| `/generate_password ` | Сгенерировать новый пароль | -| `/set_password ` | Установить пароль для пользователя | - -### 🌐 Команды кластерного управления -| Команда | Описание | -|---------|----------| -| `/cluster` | Статистика кластера и подключенные агенты | -| `/add_server ` | Добавить новый сервер в кластер | -| `/remove_server ` | Удалить сервер из кластера | -| `/deploy_agent ` | Автоматически развернуть агента на сервере | -| `/agents` | Список всех агентов кластера | -| `/check_agents` | Проверить статус всех агентов | - -### Примеры использования -``` -/details 192.168.1.100 -/ban 10.0.0.50 -/unban 203.0.113.1 - -# Новые команды безопасности -/sessions -/kick admin -/generate_password ubuntu -/compromises - -# Команды кластерного управления -/cluster -/add_server web01 192.168.1.10 ubuntu -/deploy_agent web01 -/agents -/check_agents -/remove_server old_server -``` - -## 📊 Мониторинг и логирование - -### Детекция атак -Система отслеживает следующие события в auth.log: -- `Failed password` -- `Invalid user` -- `authentication failure` -- `Too many authentication failures` -- `Failed publickey` -- `Connection closed by authenticating user` - -### Уведомления в Telegram -- ⚠️ Автоматическая блокировка IP -- 🔓 Ручная блокировка/разблокировка -- 🟢 Автоматическая разблокировка по таймеру -- ❌ Системные ошибки -- 🚨 Критические атаки - -### Логи системы -```bash -# Системные логи -sudo journalctl -u pyguardian -f - -# Файловые логи -sudo tail -f /var/log/pyguardian.log - -# Логи ошибок -sudo journalctl -u pyguardian -p err -``` - -## 🔧 Управление сервисом - -### Основные команды -```bash -# Статус -sudo systemctl status pyguardian - -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Перезагрузка конфигурации -sudo systemctl reload pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключить автозапуск -sudo systemctl disable pyguardian -``` - -### Мониторинг ресурсов -```bash -# Использование памяти -sudo systemctl show pyguardian --property=MemoryCurrent - -# Количество процессов -sudo systemctl show pyguardian --property=TasksCurrent - -# Время работы -sudo systemctl show pyguardian --property=ActiveEnterTimestamp -``` - -## 🌐 Настройка кластера - -### Включение кластерного режима - -1. **Настройка мастер-сервера:** -```bash -# В config.yaml включить кластерный режим -cluster: - enabled: true - master_host: "192.168.1.100" # IP мастер-сервера - master_port: 8080 -``` - -2. **Добавление серверов в кластер:** -```bash -# Через Telegram бота -/add_server web01 192.168.1.10 ubuntu -/add_server db01 192.168.1.20 admin -/add_server app01 192.168.1.30 deploy -``` - -3. **Автоматическое развертывание агентов:** -```bash -# Развернуть агента на сервере -/deploy_agent web01 - -# Проверить статус всех агентов -/check_agents -``` - -### Требования для кластера -- **SSH доступ**: Мастер-сервер должен иметь SSH доступ ко всем агентам -- **Сетевые порты**: Порт 8080 (мастер), 8081 (агенты) -- **Ключи SSH**: Настроенные SSH ключи или пароли для автоматического развертывания - -### Управление кластером -```bash -# Просмотр статистики кластера -/cluster - -# Список всех агентов -/agents - -# Удаление сервера из кластера -/remove_server old_server -``` - -## 🛠️ Ручная установка (альтернативный способ) - -### 1. Подготовка системы -```bash -# Ubuntu/Debian -sudo apt update -sudo apt install python3 python3-pip python3-venv iptables - -# CentOS/RHEL -sudo yum install python3 python3-pip iptables - -# или -sudo dnf install python3 python3-pip iptables -``` - -### 2. Создание пользователя (опционально) -```bash -sudo useradd -r -s /bin/false pyguardian -sudo mkdir -p /opt/pyguardian /var/lib/pyguardian -sudo chown pyguardian:pyguardian /var/lib/pyguardian -``` - -### 3. Установка PyGuardian -```bash -# Клонирование -git clone https://git.smartsoltech.kr/trevor/PyGuardian.git -cd pyguardian - -# Копирование файлов -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo cp config/config.yaml /etc/pyguardian/ -sudo cp systemd/pyguardian.service /etc/systemd/system/ - -# Установка зависимостей -cd /opt/pyguardian -sudo pip3 install -r requirements.txt - -# Права -sudo chmod +x /opt/pyguardian/main.py -sudo chown -R root:root /opt/pyguardian - -# Systemd -sudo systemctl daemon-reload -``` - -## 🔍 Диагностика и решение проблем - -### Проблемы с запуском - -#### Ошибка "Permission denied" -```bash -# Проверить права -ls -la /opt/pyguardian/main.py - -# Исправить права -sudo chmod +x /opt/pyguardian/main.py -``` - -#### Ошибка "Module not found" -```bash -# Переустановить зависимости -sudo pip3 install -r /opt/pyguardian/requirements.txt --force-reinstall -``` - -#### Ошибка доступа к auth.log -```bash -# Проверить существование файла -ls -la /var/log/auth.log - -# Проверить права -sudo chmod 644 /var/log/auth.log -``` - -### Проблемы с firewall - -#### iptables не работает -```bash -# Проверить статус -sudo iptables -L -n - -# Проверить модули -lsmod | grep ip_tables - -# Загрузить модуль -sudo modprobe ip_tables -``` - -#### nftables не работает -```bash -# Проверить статус -sudo nft list ruleset - -# Установить nftables -sudo apt install nftables # Ubuntu/Debian -sudo yum install nftables # CentOS/RHEL -``` - -### Проблемы с Telegram - -#### Бот не отвечает -```bash -# Проверить токен и ID в конфигурации -sudo cat /etc/pyguardian/config.yaml | grep -A 3 telegram - -# Проверить сетевое соединение -curl -s "https://api.telegram.org/botYOUR_TOKEN/getMe" -``` - -#### Уведомления не приходят -```bash -# Проверить ID администратора -# Убедиться что вы написали боту /start -# Проверить логи -sudo journalctl -u pyguardian | grep -i telegram -``` - -### Производительность - -#### Высокое потребление памяти -```yaml -# Настроить в config.yaml -performance: - max_memory_mb: 50 - cleanup_interval: 1800 # 30 минут - max_records_age: 259200 # 3 дня -``` - -#### Высокая нагрузка на диск -```yaml -# Увеличить интервал проверки -monitoring: - check_interval: 2.0 # Проверять каждые 2 секунды - -# Настроить логирование -logging: - log_level: "WARNING" # Меньше логов -``` - -## 📈 Мониторинг производительности - -### Grafana + Prometheus (дополнительно) -```bash -# Пример экспорта метрик через systemd -sudo systemctl status pyguardian --output=json -``` - -### Встроенная статистика -Телеграм бот предоставляет: -- Количество атак за день -- Топ атакующих IP -- Использование памяти -- Время работы системы -- Статистику обработанных событий - -## 🔐 Безопасность - -### Рекомендации -1. **Регулярно обновляйте** конфигурацию белого списка -2. **Мониторьте логи** на предмет ложных срабатываний -3. **Используйте VPN** для доступа к серверу -4. **Настройте резервное копирование** конфигурации -5. **Периодически проверяйте** список забаненных IP - -### Файлы конфигурации -- `/etc/pyguardian/config.yaml` - основная конфигурация -- `/var/lib/pyguardian/guardian.db` - база данных SQLite -- `/var/log/pyguardian.log` - логи приложения - -### Права доступа -```bash -# Конфигурация (только root может читать токены) -sudo chmod 600 /etc/pyguardian/config.yaml - -# База данных -sudo chmod 600 /var/lib/pyguardian/guardian.db - -# Логи -sudo chmod 644 /var/log/pyguardian.log -``` - -## 📚 API и интеграция - -### Webhook уведомления (планируется) -```yaml -# В будущих версиях -webhooks: - on_ban: "https://your-domain.com/webhook/ban" - on_unban: "https://your-domain.com/webhook/unban" -``` - -### REST API (планируется) -- `GET /api/stats` - получение статистики -- `POST /api/ban` - ручной бан IP -- `DELETE /api/ban/` - разбан IP - -## 🔄 Обновление - -### Автоматическое обновление -```bash -# Скачать новую версию -cd /tmp -git clone https://github.com/your-org/pyguardian.git -cd pyguardian - -# Остановить сервис -sudo systemctl stop pyguardian - -# Обновить файлы (сохраняя конфигурацию) -sudo cp -r src/ main.py requirements.txt /opt/pyguardian/ -sudo pip3 install -r /opt/pyguardian/requirements.txt - -# Запустить сервис -sudo systemctl start pyguardian -``` - -### Миграция конфигурации -При обновлениях проверяйте совместимость конфигурации: -```bash -# Проверка конфигурации -python3 -c "import yaml; yaml.safe_load(open('/etc/pyguardian/config.yaml'))" -``` - -## 🤝 Вклад в проект - -1. Форкните репозиторий -2. Создайте ветку для функции (`git checkout -b feature/amazing-feature`) -3. Зафиксируйте изменения (`git commit -m 'Add amazing feature'`) -4. Отправьте в ветку (`git push origin feature/amazing-feature`) -5. Откройте Pull Request - -## 📄 Лицензия - -Distributed under the MIT License. See `LICENSE` for more information. - -## 💬 Поддержка - -- **Issues**: [GitHub Issues](https://github.com/your-org/pyguardian/issues) -- **Telegram**: [@pyguardian_support](https://t.me/pyguardian_support) -- **Email**: support@pyguardian.dev - -## 🎯 Планы развития - -- [ ] Web-интерфейс (опционально) -- [ ] REST API -- [ ] Webhook уведомления -- [ ] Интеграция с облачными провайдерами -- [ ] Машинное обучение для детекции аномалий -- [ ] Поддержка IPv6 -- [x] **Кластерный режим** - ✅ Реализовано! -- [ ] Экспорт метрик Prometheus - ---- - -**PyGuardian** - Made with ❤️ for Linux system administrators \ No newline at end of file diff --git a/.history/config/config_20251125194231.yaml b/.history/config/config_20251125194231.yaml deleted file mode 100644 index 38a5921..0000000 --- a/.history/config/config_20251125194231.yaml +++ /dev/null @@ -1,66 +0,0 @@ -# PyGuardian Configuration File -# ============================== - -# Telegram Bot Configuration -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" # Токен бота от @BotFather - admin_id: 0 # Ваш Telegram ID (получите через @userinfobot) - -# Security & Monitoring Settings -security: - max_attempts: 5 # Максимум попыток авторизации за time_window - time_window: 60 # Окно времени в секундах - unban_time: 3600 # Время автоматической разблокировки (сек) - -# Log Monitoring -monitoring: - auth_log_path: "/var/log/auth.log" - check_interval: 1.0 # Интервал проверки лога в секундах - - # Паттерны для детекции атак - failed_patterns: - - "Failed password" - - "Invalid user" - - "authentication failure" - - "Too many authentication failures" - - "Failed publickey" - - "Connection closed by authenticating user" - -# Firewall Configuration -firewall: - backend: "iptables" # iptables или nftables - chain: "INPUT" # Цепочка для блокировки - target: "DROP" # Действие (DROP/REJECT) - - # Настройки для iptables - iptables: - table: "filter" - - # Настройки для nftables - nftables: - table: "inet pyguardian" - chain: "input" - -# Storage Configuration -storage: - database_path: "/var/lib/pyguardian/guardian.db" - backup_interval: 86400 # Бэкап БД каждые 24 часа - -# Logging Configuration -logging: - log_file: "/var/log/pyguardian.log" - log_level: "INFO" # DEBUG, INFO, WARNING, ERROR - max_log_size: 10485760 # 10MB - backup_count: 5 - -# Performance Settings -performance: - max_memory_mb: 100 # Максимальное использование памяти - cleanup_interval: 3600 # Очистка старых записей (сек) - max_records_age: 604800 # Удалять записи старше недели - -# Whitelist IPs (никогда не блокировать) -whitelist: - - "127.0.0.1" - - "::1" - # - "192.168.1.0/24" # Добавьте ваши доверенные сети \ No newline at end of file diff --git a/.history/config/config_20251125200956.yaml b/.history/config/config_20251125200956.yaml deleted file mode 100644 index d7fab70..0000000 --- a/.history/config/config_20251125200956.yaml +++ /dev/null @@ -1,86 +0,0 @@ -# PyGuardian Configuration File -# ============================== - -# Telegram Bot Configuration -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" # Токен бота от @BotFather - admin_id: 0 # Ваш Telegram ID (получите через @userinfobot) - -# Security & Monitoring Settings -security: - max_attempts: 5 # Максимум попыток авторизации за time_window - time_window: 60 # Окно времени в секундах - unban_time: 3600 # Время автоматической разблокировки (сек) - - # 🚨 STEALTH SECURITY SETTINGS - authorized_users: # Список разрешенных пользователей - - "root" - - "admin" - - "ubuntu" - - honeypot_users: # Пользователи-приманки для обнаружения взломов - - "test" - - "guest" - - "user" - - "admin123" - - "backup" - - stealth_mode_duration: 300 # Время скрытого режима после обнаружения взлома (секунды) - compromise_indicators: # Индикаторы компромисса - - "suspicious_commands" - - "unusual_login_times" - - "multiple_failed_then_success" - - "honeypot_access" - -# Log Monitoring -monitoring: - auth_log_path: "/var/log/auth.log" - check_interval: 1.0 # Интервал проверки лога в секундах - - # Паттерны для детекции атак - failed_patterns: - - "Failed password" - - "Invalid user" - - "authentication failure" - - "Too many authentication failures" - - "Failed publickey" - - "Connection closed by authenticating user" - -# Firewall Configuration -firewall: - backend: "iptables" # iptables или nftables - chain: "INPUT" # Цепочка для блокировки - target: "DROP" # Действие (DROP/REJECT) - - # Настройки для iptables - iptables: - table: "filter" - - # Настройки для nftables - nftables: - table: "inet pyguardian" - chain: "input" - -# Storage Configuration -storage: - database_path: "/var/lib/pyguardian/guardian.db" - backup_interval: 86400 # Бэкап БД каждые 24 часа - -# Logging Configuration -logging: - log_file: "/var/log/pyguardian.log" - log_level: "INFO" # DEBUG, INFO, WARNING, ERROR - max_log_size: 10485760 # 10MB - backup_count: 5 - -# Performance Settings -performance: - max_memory_mb: 100 # Максимальное использование памяти - cleanup_interval: 3600 # Очистка старых записей (сек) - max_records_age: 604800 # Удалять записи старше недели - -# Whitelist IPs (никогда не блокировать) -whitelist: - - "127.0.0.1" - - "::1" - # - "192.168.1.0/24" # Добавьте ваши доверенные сети \ No newline at end of file diff --git a/.history/config/config_20251125201009.yaml b/.history/config/config_20251125201009.yaml deleted file mode 100644 index f5b0bb5..0000000 --- a/.history/config/config_20251125201009.yaml +++ /dev/null @@ -1,104 +0,0 @@ -# PyGuardian Configuration File -# ============================== - -# Telegram Bot Configuration -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" # Токен бота от @BotFather - admin_id: 0 # Ваш Telegram ID (получите через @userinfobot) - -# Security & Monitoring Settings -security: - max_attempts: 5 # Максимум попыток авторизации за time_window - time_window: 60 # Окно времени в секундах - unban_time: 3600 # Время автоматической разблокировки (сек) - - # 🚨 STEALTH SECURITY SETTINGS - authorized_users: # Список разрешенных пользователей - - "root" - - "admin" - - "ubuntu" - - honeypot_users: # Пользователи-приманки для обнаружения взломов - - "test" - - "guest" - - "user" - - "admin123" - - "backup" - - stealth_mode_duration: 300 # Время скрытого режима после обнаружения взлома (секунды) - compromise_indicators: # Индикаторы компромисса - - "suspicious_commands" - - "unusual_login_times" - - "multiple_failed_then_success" - - "honeypot_access" - -# Log Monitoring -monitoring: - auth_log_path: "/var/log/auth.log" - check_interval: 1.0 # Интервал проверки лога в секундах - - # Паттерны для детекции атак - failed_patterns: - - "Failed password" - - "Invalid user" - - "authentication failure" - - "Too many authentication failures" - - "Failed publickey" - - "Connection closed by authenticating user" - -# Firewall Configuration -firewall: - backend: "iptables" # iptables или nftables - chain: "INPUT" # Цепочка для блокировки - target: "DROP" # Действие (DROP/REJECT) - - # Настройки для iptables - iptables: - table: "filter" - - # Настройки для nftables - nftables: - table: "inet pyguardian" - chain: "input" - -# Storage Configuration -storage: - database_path: "/var/lib/pyguardian/guardian.db" - backup_interval: 86400 # Бэкап БД каждые 24 часа - -# Password Management Settings -passwords: - password_length: 16 # Длина генерируемых паролей - use_special_chars: true # Использовать специальные символы - password_history_size: 5 # Размер истории паролей - -# Performance Settings -performance: - cleanup_interval: 3600 # Интервал очистки старых записей (секунды) - max_records_age: 604800 # Максимальный возраст записей (секунды) - 7 дней - -# Logging Configuration -logging: - level: "INFO" # Уровень логирования: DEBUG, INFO, WARNING, ERROR - file: "/var/log/pyguardian.log" - max_size: 10485760 # Максимальный размер лог файла (10MB) - backup_count: 5 # Количество резервных копий лог файлов - -# Logging Configuration -logging: - log_file: "/var/log/pyguardian.log" - log_level: "INFO" # DEBUG, INFO, WARNING, ERROR - max_log_size: 10485760 # 10MB - backup_count: 5 - -# Performance Settings -performance: - max_memory_mb: 100 # Максимальное использование памяти - cleanup_interval: 3600 # Очистка старых записей (сек) - max_records_age: 604800 # Удалять записи старше недели - -# Whitelist IPs (никогда не блокировать) -whitelist: - - "127.0.0.1" - - "::1" - # - "192.168.1.0/24" # Добавьте ваши доверенные сети \ No newline at end of file diff --git a/.history/config/config_20251125202055.yaml b/.history/config/config_20251125202055.yaml deleted file mode 100644 index f5b0bb5..0000000 --- a/.history/config/config_20251125202055.yaml +++ /dev/null @@ -1,104 +0,0 @@ -# PyGuardian Configuration File -# ============================== - -# Telegram Bot Configuration -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" # Токен бота от @BotFather - admin_id: 0 # Ваш Telegram ID (получите через @userinfobot) - -# Security & Monitoring Settings -security: - max_attempts: 5 # Максимум попыток авторизации за time_window - time_window: 60 # Окно времени в секундах - unban_time: 3600 # Время автоматической разблокировки (сек) - - # 🚨 STEALTH SECURITY SETTINGS - authorized_users: # Список разрешенных пользователей - - "root" - - "admin" - - "ubuntu" - - honeypot_users: # Пользователи-приманки для обнаружения взломов - - "test" - - "guest" - - "user" - - "admin123" - - "backup" - - stealth_mode_duration: 300 # Время скрытого режима после обнаружения взлома (секунды) - compromise_indicators: # Индикаторы компромисса - - "suspicious_commands" - - "unusual_login_times" - - "multiple_failed_then_success" - - "honeypot_access" - -# Log Monitoring -monitoring: - auth_log_path: "/var/log/auth.log" - check_interval: 1.0 # Интервал проверки лога в секундах - - # Паттерны для детекции атак - failed_patterns: - - "Failed password" - - "Invalid user" - - "authentication failure" - - "Too many authentication failures" - - "Failed publickey" - - "Connection closed by authenticating user" - -# Firewall Configuration -firewall: - backend: "iptables" # iptables или nftables - chain: "INPUT" # Цепочка для блокировки - target: "DROP" # Действие (DROP/REJECT) - - # Настройки для iptables - iptables: - table: "filter" - - # Настройки для nftables - nftables: - table: "inet pyguardian" - chain: "input" - -# Storage Configuration -storage: - database_path: "/var/lib/pyguardian/guardian.db" - backup_interval: 86400 # Бэкап БД каждые 24 часа - -# Password Management Settings -passwords: - password_length: 16 # Длина генерируемых паролей - use_special_chars: true # Использовать специальные символы - password_history_size: 5 # Размер истории паролей - -# Performance Settings -performance: - cleanup_interval: 3600 # Интервал очистки старых записей (секунды) - max_records_age: 604800 # Максимальный возраст записей (секунды) - 7 дней - -# Logging Configuration -logging: - level: "INFO" # Уровень логирования: DEBUG, INFO, WARNING, ERROR - file: "/var/log/pyguardian.log" - max_size: 10485760 # Максимальный размер лог файла (10MB) - backup_count: 5 # Количество резервных копий лог файлов - -# Logging Configuration -logging: - log_file: "/var/log/pyguardian.log" - log_level: "INFO" # DEBUG, INFO, WARNING, ERROR - max_log_size: 10485760 # 10MB - backup_count: 5 - -# Performance Settings -performance: - max_memory_mb: 100 # Максимальное использование памяти - cleanup_interval: 3600 # Очистка старых записей (сек) - max_records_age: 604800 # Удалять записи старше недели - -# Whitelist IPs (никогда не блокировать) -whitelist: - - "127.0.0.1" - - "::1" - # - "192.168.1.0/24" # Добавьте ваши доверенные сети \ No newline at end of file diff --git a/.history/config/config_20251125202526.yaml b/.history/config/config_20251125202526.yaml deleted file mode 100644 index 5004ecb..0000000 --- a/.history/config/config_20251125202526.yaml +++ /dev/null @@ -1,113 +0,0 @@ -# PyGuardian Configuration File -# ============================== - -# Telegram Bot Configuration -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" # Токен бота от @BotFather - admin_id: 0 # Ваш Telegram ID (получите через @userinfobot) - -# Security & Monitoring Settings -security: - max_attempts: 5 # Максимум попыток авторизации за time_window - time_window: 60 # Окно времени в секундах - unban_time: 3600 # Время автоматической разблокировки (сек) - - # 🚨 STEALTH SECURITY SETTINGS - authorized_users: # Список разрешенных пользователей - - "root" - - "admin" - - "ubuntu" - - honeypot_users: # Пользователи-приманки для обнаружения взломов - - "test" - - "guest" - - "user" - - "admin123" - - "backup" - - stealth_mode_duration: 300 # Время скрытого режима после обнаружения взлома (секунды) - compromise_indicators: # Индикаторы компромисса - - "suspicious_commands" - - "unusual_login_times" - - "multiple_failed_then_success" - - "honeypot_access" - -# Log Monitoring -monitoring: - auth_log_path: "/var/log/auth.log" - check_interval: 1.0 # Интервал проверки лога в секундах - - # Паттерны для детекции атак - failed_patterns: - - "Failed password" - - "Invalid user" - - "authentication failure" - - "Too many authentication failures" - - "Failed publickey" - - "Connection closed by authenticating user" - -# Firewall Configuration -firewall: - backend: "iptables" # iptables или nftables - chain: "INPUT" # Цепочка для блокировки - target: "DROP" # Действие (DROP/REJECT) - - # Настройки для iptables - iptables: - table: "filter" - - # Настройки для nftables - nftables: - table: "inet pyguardian" - chain: "input" - -# Storage Configuration -storage: - database_path: "/var/lib/pyguardian/guardian.db" - backup_interval: 86400 # Бэкап БД каждые 24 часа - -# Password Management Settings -passwords: - password_length: 16 # Длина генерируемых паролей - use_special_chars: true # Использовать специальные символы - password_history_size: 5 # Размер истории паролей - -# Cluster Management Settings -cluster: - cluster_name: "PyGuardian-Cluster" # Название кластера - master_server: true # Этот сервер - master - agents_config_path: "/var/lib/pyguardian/agents.yaml" - deployment_path: "/opt/pyguardian" - ssh_timeout: 30 # Таймаут SSH соединений - ssh_retries: 3 # Количество попыток подключения - -# Performance Settings -performance: - cleanup_interval: 3600 # Интервал очистки старых записей (секунды) - max_records_age: 604800 # Максимальный возраст записей (секунды) - 7 дней - -# Logging Configuration -logging: - level: "INFO" # Уровень логирования: DEBUG, INFO, WARNING, ERROR - file: "/var/log/pyguardian.log" - max_size: 10485760 # Максимальный размер лог файла (10MB) - backup_count: 5 # Количество резервных копий лог файлов - -# Logging Configuration -logging: - log_file: "/var/log/pyguardian.log" - log_level: "INFO" # DEBUG, INFO, WARNING, ERROR - max_log_size: 10485760 # 10MB - backup_count: 5 - -# Performance Settings -performance: - max_memory_mb: 100 # Максимальное использование памяти - cleanup_interval: 3600 # Очистка старых записей (сек) - max_records_age: 604800 # Удалять записи старше недели - -# Whitelist IPs (никогда не блокировать) -whitelist: - - "127.0.0.1" - - "::1" - # - "192.168.1.0/24" # Добавьте ваши доверенные сети \ No newline at end of file diff --git a/.history/config/config_20251125203709.yaml b/.history/config/config_20251125203709.yaml deleted file mode 100644 index 5004ecb..0000000 --- a/.history/config/config_20251125203709.yaml +++ /dev/null @@ -1,113 +0,0 @@ -# PyGuardian Configuration File -# ============================== - -# Telegram Bot Configuration -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" # Токен бота от @BotFather - admin_id: 0 # Ваш Telegram ID (получите через @userinfobot) - -# Security & Monitoring Settings -security: - max_attempts: 5 # Максимум попыток авторизации за time_window - time_window: 60 # Окно времени в секундах - unban_time: 3600 # Время автоматической разблокировки (сек) - - # 🚨 STEALTH SECURITY SETTINGS - authorized_users: # Список разрешенных пользователей - - "root" - - "admin" - - "ubuntu" - - honeypot_users: # Пользователи-приманки для обнаружения взломов - - "test" - - "guest" - - "user" - - "admin123" - - "backup" - - stealth_mode_duration: 300 # Время скрытого режима после обнаружения взлома (секунды) - compromise_indicators: # Индикаторы компромисса - - "suspicious_commands" - - "unusual_login_times" - - "multiple_failed_then_success" - - "honeypot_access" - -# Log Monitoring -monitoring: - auth_log_path: "/var/log/auth.log" - check_interval: 1.0 # Интервал проверки лога в секундах - - # Паттерны для детекции атак - failed_patterns: - - "Failed password" - - "Invalid user" - - "authentication failure" - - "Too many authentication failures" - - "Failed publickey" - - "Connection closed by authenticating user" - -# Firewall Configuration -firewall: - backend: "iptables" # iptables или nftables - chain: "INPUT" # Цепочка для блокировки - target: "DROP" # Действие (DROP/REJECT) - - # Настройки для iptables - iptables: - table: "filter" - - # Настройки для nftables - nftables: - table: "inet pyguardian" - chain: "input" - -# Storage Configuration -storage: - database_path: "/var/lib/pyguardian/guardian.db" - backup_interval: 86400 # Бэкап БД каждые 24 часа - -# Password Management Settings -passwords: - password_length: 16 # Длина генерируемых паролей - use_special_chars: true # Использовать специальные символы - password_history_size: 5 # Размер истории паролей - -# Cluster Management Settings -cluster: - cluster_name: "PyGuardian-Cluster" # Название кластера - master_server: true # Этот сервер - master - agents_config_path: "/var/lib/pyguardian/agents.yaml" - deployment_path: "/opt/pyguardian" - ssh_timeout: 30 # Таймаут SSH соединений - ssh_retries: 3 # Количество попыток подключения - -# Performance Settings -performance: - cleanup_interval: 3600 # Интервал очистки старых записей (секунды) - max_records_age: 604800 # Максимальный возраст записей (секунды) - 7 дней - -# Logging Configuration -logging: - level: "INFO" # Уровень логирования: DEBUG, INFO, WARNING, ERROR - file: "/var/log/pyguardian.log" - max_size: 10485760 # Максимальный размер лог файла (10MB) - backup_count: 5 # Количество резервных копий лог файлов - -# Logging Configuration -logging: - log_file: "/var/log/pyguardian.log" - log_level: "INFO" # DEBUG, INFO, WARNING, ERROR - max_log_size: 10485760 # 10MB - backup_count: 5 - -# Performance Settings -performance: - max_memory_mb: 100 # Максимальное использование памяти - cleanup_interval: 3600 # Очистка старых записей (сек) - max_records_age: 604800 # Удалять записи старше недели - -# Whitelist IPs (никогда не блокировать) -whitelist: - - "127.0.0.1" - - "::1" - # - "192.168.1.0/24" # Добавьте ваши доверенные сети \ No newline at end of file diff --git a/.history/deployment/docker/Dockerfile_20251125210101 b/.history/deployment/docker/Dockerfile_20251125210101 deleted file mode 100644 index 08f5b72..0000000 --- a/.history/deployment/docker/Dockerfile_20251125210101 +++ /dev/null @@ -1,91 +0,0 @@ -# PyGuardian Multi-stage Dockerfile -# Supports both controller and agent modes - -FROM python:3.11-slim AS base - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - iptables \ - iputils-ping \ - openssh-client \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Create pyguardian user -RUN groupadd -r pyguardian && useradd -r -g pyguardian pyguardian - -# Set working directory -WORKDIR /opt/pyguardian - -# Copy requirements and install Python dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy source code -COPY src/ ./src/ -COPY config/ ./config/ -COPY main.py . - -# Set permissions -RUN chown -R pyguardian:pyguardian /opt/pyguardian - -# Create data and logs directories -RUN mkdir -p /opt/pyguardian/data /opt/pyguardian/logs \ - && chown -R pyguardian:pyguardian /opt/pyguardian/data /opt/pyguardian/logs - -# Controller mode -FROM base AS controller - -# Expose API port -EXPOSE 8443 - -# Run as pyguardian user -USER pyguardian - -# Set environment variables -ENV PYGUARDIAN_MODE=controller -ENV PYTHONPATH=/opt/pyguardian - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD curl -f http://localhost:8443/health || exit 1 - -# Start command -CMD ["python", "main.py", "--mode", "controller"] - -# Agent mode -FROM base AS agent - -# Run as pyguardian user -USER pyguardian - -# Set environment variables -ENV PYGUARDIAN_MODE=agent -ENV PYTHONPATH=/opt/pyguardian - -# Health check for agent -HEALTHCHECK --interval=60s --timeout=15s --start-period=30s --retries=3 \ - CMD python -c "import sys; sys.exit(0)" || exit 1 - -# Start command -CMD ["python", "main.py", "--mode", "agent"] - -# Standalone mode (default) -FROM base AS standalone - -# Expose API port (optional for standalone) -EXPOSE 8443 - -# Run as pyguardian user -USER pyguardian - -# Set environment variables -ENV PYGUARDIAN_MODE=standalone -ENV PYTHONPATH=/opt/pyguardian - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD python -c "import sys; sys.exit(0)" || exit 1 - -# Start command -CMD ["python", "main.py"] \ No newline at end of file diff --git a/.history/deployment/docker/Dockerfile_20251125210433 b/.history/deployment/docker/Dockerfile_20251125210433 deleted file mode 100644 index 08f5b72..0000000 --- a/.history/deployment/docker/Dockerfile_20251125210433 +++ /dev/null @@ -1,91 +0,0 @@ -# PyGuardian Multi-stage Dockerfile -# Supports both controller and agent modes - -FROM python:3.11-slim AS base - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - iptables \ - iputils-ping \ - openssh-client \ - curl \ - && rm -rf /var/lib/apt/lists/* - -# Create pyguardian user -RUN groupadd -r pyguardian && useradd -r -g pyguardian pyguardian - -# Set working directory -WORKDIR /opt/pyguardian - -# Copy requirements and install Python dependencies -COPY requirements.txt . -RUN pip install --no-cache-dir -r requirements.txt - -# Copy source code -COPY src/ ./src/ -COPY config/ ./config/ -COPY main.py . - -# Set permissions -RUN chown -R pyguardian:pyguardian /opt/pyguardian - -# Create data and logs directories -RUN mkdir -p /opt/pyguardian/data /opt/pyguardian/logs \ - && chown -R pyguardian:pyguardian /opt/pyguardian/data /opt/pyguardian/logs - -# Controller mode -FROM base AS controller - -# Expose API port -EXPOSE 8443 - -# Run as pyguardian user -USER pyguardian - -# Set environment variables -ENV PYGUARDIAN_MODE=controller -ENV PYTHONPATH=/opt/pyguardian - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD curl -f http://localhost:8443/health || exit 1 - -# Start command -CMD ["python", "main.py", "--mode", "controller"] - -# Agent mode -FROM base AS agent - -# Run as pyguardian user -USER pyguardian - -# Set environment variables -ENV PYGUARDIAN_MODE=agent -ENV PYTHONPATH=/opt/pyguardian - -# Health check for agent -HEALTHCHECK --interval=60s --timeout=15s --start-period=30s --retries=3 \ - CMD python -c "import sys; sys.exit(0)" || exit 1 - -# Start command -CMD ["python", "main.py", "--mode", "agent"] - -# Standalone mode (default) -FROM base AS standalone - -# Expose API port (optional for standalone) -EXPOSE 8443 - -# Run as pyguardian user -USER pyguardian - -# Set environment variables -ENV PYGUARDIAN_MODE=standalone -ENV PYTHONPATH=/opt/pyguardian - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \ - CMD python -c "import sys; sys.exit(0)" || exit 1 - -# Start command -CMD ["python", "main.py"] \ No newline at end of file diff --git a/.history/deployment/docker/docker-compose_20251125210113.yml b/.history/deployment/docker/docker-compose_20251125210113.yml deleted file mode 100644 index ac65a94..0000000 --- a/.history/deployment/docker/docker-compose_20251125210113.yml +++ /dev/null @@ -1,77 +0,0 @@ -# PyGuardian Docker Compose -# Controller + Agent cluster setup - -version: '3.8' - -services: - pyguardian-controller: - build: - context: ../.. - dockerfile: deployment/docker/Dockerfile - target: controller - container_name: pyguardian-controller - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - controller_data:/opt/pyguardian/data - - controller_logs:/opt/pyguardian/logs - - controller_config:/opt/pyguardian/config - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=controller - - PYGUARDIAN_CONFIG=/opt/pyguardian/config/config.yaml - - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} - - CLUSTER_SECRET=${CLUSTER_SECRET} - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8443/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - - pyguardian-agent-1: - build: - context: ../.. - dockerfile: deployment/docker/Dockerfile - target: agent - container_name: pyguardian-agent-1 - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - agent1_data:/opt/pyguardian/data - - agent1_logs:/opt/pyguardian/logs - - agent1_config:/opt/pyguardian/config - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=agent - - CONTROLLER_HOST=localhost - - CONTROLLER_PORT=8443 - - CLUSTER_SECRET=${CLUSTER_SECRET} - depends_on: - - pyguardian-controller - healthcheck: - test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] - interval: 60s - timeout: 15s - retries: 3 - start_period: 30s - -volumes: - controller_data: - driver: local - controller_logs: - driver: local - controller_config: - driver: local - agent1_data: - driver: local - agent1_logs: - driver: local - agent1_config: - driver: local - -networks: - default: - name: pyguardian-network \ No newline at end of file diff --git a/.history/deployment/docker/docker-compose_20251125210433.yml b/.history/deployment/docker/docker-compose_20251125210433.yml deleted file mode 100644 index ac65a94..0000000 --- a/.history/deployment/docker/docker-compose_20251125210433.yml +++ /dev/null @@ -1,77 +0,0 @@ -# PyGuardian Docker Compose -# Controller + Agent cluster setup - -version: '3.8' - -services: - pyguardian-controller: - build: - context: ../.. - dockerfile: deployment/docker/Dockerfile - target: controller - container_name: pyguardian-controller - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - controller_data:/opt/pyguardian/data - - controller_logs:/opt/pyguardian/logs - - controller_config:/opt/pyguardian/config - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=controller - - PYGUARDIAN_CONFIG=/opt/pyguardian/config/config.yaml - - TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN} - - CLUSTER_SECRET=${CLUSTER_SECRET} - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8443/health"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 40s - - pyguardian-agent-1: - build: - context: ../.. - dockerfile: deployment/docker/Dockerfile - target: agent - container_name: pyguardian-agent-1 - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - agent1_data:/opt/pyguardian/data - - agent1_logs:/opt/pyguardian/logs - - agent1_config:/opt/pyguardian/config - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=agent - - CONTROLLER_HOST=localhost - - CONTROLLER_PORT=8443 - - CLUSTER_SECRET=${CLUSTER_SECRET} - depends_on: - - pyguardian-controller - healthcheck: - test: ["CMD", "python", "-c", "import sys; sys.exit(0)"] - interval: 60s - timeout: 15s - retries: 3 - start_period: 30s - -volumes: - controller_data: - driver: local - controller_logs: - driver: local - controller_config: - driver: local - agent1_data: - driver: local - agent1_logs: - driver: local - agent1_config: - driver: local - -networks: - default: - name: pyguardian-network \ No newline at end of file diff --git a/.history/docs/INSTALLATION_20251125203948.md b/.history/docs/INSTALLATION_20251125203948.md deleted file mode 100644 index 094c604..0000000 --- a/.history/docs/INSTALLATION_20251125203948.md +++ /dev/null @@ -1,357 +0,0 @@ -# PyGuardian Installation Guide - -## Обзор - -PyGuardian предлагает несколько способов установки в зависимости от ваших потребностей: - -1. **Standalone** - Автономный сервер (все в одном) -2. **Controller** - Центральный контроллер кластера -3. **Agent** - Агент для подключения к контроллеру -4. **Docker** - Контейнеризованное развертывание - -## Быстрая установка - -### Использование make - -```bash -# Клонирование репозитория -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Интерактивная установка -sudo make install - -# Или быстрая автономная установка -sudo make standalone - -# Или контроллер кластера -sudo make controller - -# Или агент кластера -sudo make agent -``` - -### Использование install.sh - -```bash -# Интерактивный режим -sudo ./install.sh - -# Быстрая установка -sudo ./install.sh --quick - -# Конкретный режим -sudo ./install.sh --interactive -sudo ./install.sh --docker -``` - -## Подробная установка - -### 1. Standalone режим - -**Назначение**: Полнофункциональная система на одном сервере -**Подходит для**: Небольшие инфраструктуры, тестирования - -```bash -# Автоматическая установка -sudo make standalone - -# Или вручную -sudo ./scripts/install.sh --mode=standalone --non-interactive \ - --telegram-token="YOUR_BOT_TOKEN" \ - --admin-id="YOUR_TELEGRAM_ID" -``` - -**Что включено**: -- Мониторинг auth.log -- Telegram бот управления -- Stealth security система -- Firewall интеграция -- Автоматическое управление паролями -- SSH session management - -### 2. Controller режим - -**Назначение**: Центральный контроллер для управления кластером агентов -**Подходит для**: Крупные инфраструктуры, централизованное управление - -```bash -# Автоматическая установка -sudo make controller - -# Или вручную -sudo ./scripts/install.sh --mode=controller --non-interactive \ - --telegram-token="YOUR_BOT_TOKEN" \ - --admin-id="YOUR_TELEGRAM_ID" -``` - -**Что включено**: -- Все функции Standalone -- API для управления агентами -- Веб-интерфейс управления -- Централизованная отчетность -- Автоматическое развертывание агентов - -**После установки**: -1. Откройте порт 8080 в firewall -2. Настройте SSL сертификат -3. Добавьте агенты через Telegram команды - -### 3. Agent режим - -**Назначение**: Агент для подключения к контроллеру -**Подходит для**: Серверы в составе кластера - -```bash -# Автоматическая установка -sudo make agent - -# Или вручную -sudo ./scripts/install.sh --mode=agent --non-interactive \ - --controller-url="https://controller.example.com:8080" \ - --agent-token="AGENT_TOKEN" -``` - -**Что включено**: -- Локальный мониторинг auth.log -- Firewall управление -- Подключение к контроллеру -- Передача данных в центр - -**Перед установкой**: -1. Получите токен агента от администратора контроллера -2. Убедитесь в доступности контроллера по сети - -## Docker установка - -### Controller в Docker - -```bash -# Интерактивная установка -sudo ./scripts/docker-install.sh --mode=controller - -# Или с параметрами -sudo ./scripts/docker-install.sh \ - --mode=controller \ - --telegram-token="YOUR_BOT_TOKEN" \ - --admin-id="YOUR_TELEGRAM_ID" \ - --port=8080 -``` - -### Agent в Docker - -```bash -# Интерактивная установка -sudo ./scripts/docker-install.sh --mode=agent - -# Или с параметрами -sudo ./scripts/docker-install.sh \ - --mode=agent \ - --controller-url="https://controller.example.com:8080" \ - --agent-token="AGENT_TOKEN" -``` - -## Требования системы - -### Минимальные требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **RAM**: 512 MB -- **Диск**: 1 GB свободного места -- **Сеть**: Доступ в интернет для Telegram API - -### Рекомендуемые требования - -- **ОС**: Ubuntu 22.04 LTS -- **Python**: 3.11+ -- **RAM**: 2 GB -- **Диск**: 5 GB свободного места -- **CPU**: 2 ядра - -### Зависимости - -- `iptables` или `nftables` -- `systemd` -- `python3-pip` -- `sqlite3` - -## Конфигурация после установки - -### 1. Настройка Telegram бота - -```bash -# Создайте бота у @BotFather -# Получите токен и ваш chat ID у @userinfobot - -# Обновите конфигурацию -sudo nano /etc/pyguardian/config.yaml -``` - -### 2. Настройка firewall - -```bash -# Для контроллера - откройте API порт -sudo ufw allow 8080 - -# Для всех режимов - убедитесь что SSH доступен -sudo ufw allow ssh -``` - -### 3. Проверка установки - -```bash -# Статус сервиса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f - -# Проверка конфигурации -sudo /opt/pyguardian/venv/bin/python /opt/pyguardian/main.py --check-config -``` - -## Управление службой - -```bash -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключение автозапуска -sudo systemctl disable pyguardian - -# Статус -sudo systemctl status pyguardian -``` - -## Обновление - -### Standalone/Controller/Agent - -```bash -# Остановка службы -sudo systemctl stop pyguardian - -# Обновление кода -cd /opt/pyguardian -sudo git pull origin main - -# Обновление зависимостей -sudo -u pyguardian /opt/pyguardian/venv/bin/pip install -r requirements.txt - -# Запуск -sudo systemctl start pyguardian -``` - -### Docker - -```bash -# Переход в директорию установки -cd /path/to/pyguardian-docker - -# Остановка контейнеров -sudo docker-compose down - -# Обновление образов -sudo docker-compose pull - -# Пересборка и запуск -sudo docker-compose up --build -d -``` - -## Удаление - -### Полное удаление системы - -```bash -# Остановка и отключение службы -sudo systemctl stop pyguardian -sudo systemctl disable pyguardian - -# Удаление файлов службы -sudo rm -f /etc/systemd/system/pyguardian.service -sudo systemctl daemon-reload - -# Удаление приложения -sudo rm -rf /opt/pyguardian - -# Удаление конфигурации (опционально) -sudo rm -rf /etc/pyguardian - -# Удаление данных (опционально) -sudo rm -rf /var/lib/pyguardian - -# Удаление логов (опционально) -sudo rm -rf /var/log/pyguardian - -# Удаление пользователя -sudo userdel -r pyguardian -``` - -### Удаление Docker установки - -```bash -# Остановка и удаление контейнеров -sudo docker-compose down -v - -# Удаление образов -sudo docker rmi $(sudo docker images pyguardian* -q) - -# Удаление файлов установки -sudo rm -rf /path/to/pyguardian-docker -``` - -## Troubleshooting - -### Проблемы с правами - -```bash -# Проверка прав файлов -sudo chown -R pyguardian:pyguardian /opt/pyguardian -sudo chown -R pyguardian:pyguardian /var/lib/pyguardian -sudo chmod +x /opt/pyguardian/main.py -``` - -### Проблемы с Python - -```bash -# Проверка версии Python -python3 --version - -# Переустановка зависимостей -sudo -u pyguardian /opt/pyguardian/venv/bin/pip install --force-reinstall -r /opt/pyguardian/requirements.txt -``` - -### Проблемы с firewall - -```bash -# Проверка iptables -sudo iptables -L PyGuardian -n - -# Проверка nftables -sudo nft list table inet pyguardian - -# Сброс правил (осторожно!) -sudo systemctl stop pyguardian -sudo iptables -F PyGuardian -sudo systemctl start pyguardian -``` - -### Проблемы с Telegram - -```bash -# Проверка токена бота -curl "https://api.telegram.org/bot/getMe" - -# Проверка конфигурации -grep -A5 "telegram:" /etc/pyguardian/config.yaml -``` \ No newline at end of file diff --git a/.history/docs/INSTALLATION_20251125204704.md b/.history/docs/INSTALLATION_20251125204704.md deleted file mode 100644 index 094c604..0000000 --- a/.history/docs/INSTALLATION_20251125204704.md +++ /dev/null @@ -1,357 +0,0 @@ -# PyGuardian Installation Guide - -## Обзор - -PyGuardian предлагает несколько способов установки в зависимости от ваших потребностей: - -1. **Standalone** - Автономный сервер (все в одном) -2. **Controller** - Центральный контроллер кластера -3. **Agent** - Агент для подключения к контроллеру -4. **Docker** - Контейнеризованное развертывание - -## Быстрая установка - -### Использование make - -```bash -# Клонирование репозитория -git clone https://github.com/your-repo/PyGuardian.git -cd PyGuardian - -# Интерактивная установка -sudo make install - -# Или быстрая автономная установка -sudo make standalone - -# Или контроллер кластера -sudo make controller - -# Или агент кластера -sudo make agent -``` - -### Использование install.sh - -```bash -# Интерактивный режим -sudo ./install.sh - -# Быстрая установка -sudo ./install.sh --quick - -# Конкретный режим -sudo ./install.sh --interactive -sudo ./install.sh --docker -``` - -## Подробная установка - -### 1. Standalone режим - -**Назначение**: Полнофункциональная система на одном сервере -**Подходит для**: Небольшие инфраструктуры, тестирования - -```bash -# Автоматическая установка -sudo make standalone - -# Или вручную -sudo ./scripts/install.sh --mode=standalone --non-interactive \ - --telegram-token="YOUR_BOT_TOKEN" \ - --admin-id="YOUR_TELEGRAM_ID" -``` - -**Что включено**: -- Мониторинг auth.log -- Telegram бот управления -- Stealth security система -- Firewall интеграция -- Автоматическое управление паролями -- SSH session management - -### 2. Controller режим - -**Назначение**: Центральный контроллер для управления кластером агентов -**Подходит для**: Крупные инфраструктуры, централизованное управление - -```bash -# Автоматическая установка -sudo make controller - -# Или вручную -sudo ./scripts/install.sh --mode=controller --non-interactive \ - --telegram-token="YOUR_BOT_TOKEN" \ - --admin-id="YOUR_TELEGRAM_ID" -``` - -**Что включено**: -- Все функции Standalone -- API для управления агентами -- Веб-интерфейс управления -- Централизованная отчетность -- Автоматическое развертывание агентов - -**После установки**: -1. Откройте порт 8080 в firewall -2. Настройте SSL сертификат -3. Добавьте агенты через Telegram команды - -### 3. Agent режим - -**Назначение**: Агент для подключения к контроллеру -**Подходит для**: Серверы в составе кластера - -```bash -# Автоматическая установка -sudo make agent - -# Или вручную -sudo ./scripts/install.sh --mode=agent --non-interactive \ - --controller-url="https://controller.example.com:8080" \ - --agent-token="AGENT_TOKEN" -``` - -**Что включено**: -- Локальный мониторинг auth.log -- Firewall управление -- Подключение к контроллеру -- Передача данных в центр - -**Перед установкой**: -1. Получите токен агента от администратора контроллера -2. Убедитесь в доступности контроллера по сети - -## Docker установка - -### Controller в Docker - -```bash -# Интерактивная установка -sudo ./scripts/docker-install.sh --mode=controller - -# Или с параметрами -sudo ./scripts/docker-install.sh \ - --mode=controller \ - --telegram-token="YOUR_BOT_TOKEN" \ - --admin-id="YOUR_TELEGRAM_ID" \ - --port=8080 -``` - -### Agent в Docker - -```bash -# Интерактивная установка -sudo ./scripts/docker-install.sh --mode=agent - -# Или с параметрами -sudo ./scripts/docker-install.sh \ - --mode=agent \ - --controller-url="https://controller.example.com:8080" \ - --agent-token="AGENT_TOKEN" -``` - -## Требования системы - -### Минимальные требования - -- **ОС**: Linux (Ubuntu 20.04+, CentOS 8+, Debian 11+) -- **Python**: 3.10 или выше -- **RAM**: 512 MB -- **Диск**: 1 GB свободного места -- **Сеть**: Доступ в интернет для Telegram API - -### Рекомендуемые требования - -- **ОС**: Ubuntu 22.04 LTS -- **Python**: 3.11+ -- **RAM**: 2 GB -- **Диск**: 5 GB свободного места -- **CPU**: 2 ядра - -### Зависимости - -- `iptables` или `nftables` -- `systemd` -- `python3-pip` -- `sqlite3` - -## Конфигурация после установки - -### 1. Настройка Telegram бота - -```bash -# Создайте бота у @BotFather -# Получите токен и ваш chat ID у @userinfobot - -# Обновите конфигурацию -sudo nano /etc/pyguardian/config.yaml -``` - -### 2. Настройка firewall - -```bash -# Для контроллера - откройте API порт -sudo ufw allow 8080 - -# Для всех режимов - убедитесь что SSH доступен -sudo ufw allow ssh -``` - -### 3. Проверка установки - -```bash -# Статус сервиса -sudo systemctl status pyguardian - -# Просмотр логов -sudo journalctl -u pyguardian -f - -# Проверка конфигурации -sudo /opt/pyguardian/venv/bin/python /opt/pyguardian/main.py --check-config -``` - -## Управление службой - -```bash -# Запуск -sudo systemctl start pyguardian - -# Остановка -sudo systemctl stop pyguardian - -# Перезапуск -sudo systemctl restart pyguardian - -# Автозапуск -sudo systemctl enable pyguardian - -# Отключение автозапуска -sudo systemctl disable pyguardian - -# Статус -sudo systemctl status pyguardian -``` - -## Обновление - -### Standalone/Controller/Agent - -```bash -# Остановка службы -sudo systemctl stop pyguardian - -# Обновление кода -cd /opt/pyguardian -sudo git pull origin main - -# Обновление зависимостей -sudo -u pyguardian /opt/pyguardian/venv/bin/pip install -r requirements.txt - -# Запуск -sudo systemctl start pyguardian -``` - -### Docker - -```bash -# Переход в директорию установки -cd /path/to/pyguardian-docker - -# Остановка контейнеров -sudo docker-compose down - -# Обновление образов -sudo docker-compose pull - -# Пересборка и запуск -sudo docker-compose up --build -d -``` - -## Удаление - -### Полное удаление системы - -```bash -# Остановка и отключение службы -sudo systemctl stop pyguardian -sudo systemctl disable pyguardian - -# Удаление файлов службы -sudo rm -f /etc/systemd/system/pyguardian.service -sudo systemctl daemon-reload - -# Удаление приложения -sudo rm -rf /opt/pyguardian - -# Удаление конфигурации (опционально) -sudo rm -rf /etc/pyguardian - -# Удаление данных (опционально) -sudo rm -rf /var/lib/pyguardian - -# Удаление логов (опционально) -sudo rm -rf /var/log/pyguardian - -# Удаление пользователя -sudo userdel -r pyguardian -``` - -### Удаление Docker установки - -```bash -# Остановка и удаление контейнеров -sudo docker-compose down -v - -# Удаление образов -sudo docker rmi $(sudo docker images pyguardian* -q) - -# Удаление файлов установки -sudo rm -rf /path/to/pyguardian-docker -``` - -## Troubleshooting - -### Проблемы с правами - -```bash -# Проверка прав файлов -sudo chown -R pyguardian:pyguardian /opt/pyguardian -sudo chown -R pyguardian:pyguardian /var/lib/pyguardian -sudo chmod +x /opt/pyguardian/main.py -``` - -### Проблемы с Python - -```bash -# Проверка версии Python -python3 --version - -# Переустановка зависимостей -sudo -u pyguardian /opt/pyguardian/venv/bin/pip install --force-reinstall -r /opt/pyguardian/requirements.txt -``` - -### Проблемы с firewall - -```bash -# Проверка iptables -sudo iptables -L PyGuardian -n - -# Проверка nftables -sudo nft list table inet pyguardian - -# Сброс правил (осторожно!) -sudo systemctl stop pyguardian -sudo iptables -F PyGuardian -sudo systemctl start pyguardian -``` - -### Проблемы с Telegram - -```bash -# Проверка токена бота -curl "https://api.telegram.org/bot/getMe" - -# Проверка конфигурации -grep -A5 "telegram:" /etc/pyguardian/config.yaml -``` \ No newline at end of file diff --git a/.history/docs/cluster-management_20251125202639.md b/.history/docs/cluster-management_20251125202639.md deleted file mode 100644 index 3cd8ceb..0000000 --- a/.history/docs/cluster-management_20251125202639.md +++ /dev/null @@ -1,345 +0,0 @@ -# Управление кластером PyGuardian - -## 🏢 Централизованное развертывание агентов - -PyGuardian поддерживает централизованное управление кластером серверов через Telegram бот. Мастер-сервер может автоматически развертывать и управлять агентами на удаленных серверах. - -## 🎯 Возможности кластера - -### Основные функции: -- **Автоматическое развертывание**: Установка PyGuardian на удаленные серверы -- **Централизованное управление**: Контроль всех агентов через один Telegram бот -- **Мониторинг статуса**: Проверка состояния агентов в реальном времени -- **SSH интеграция**: Безопасное подключение через SSH ключи или пароли -- **Автоматическая очистка**: Удаление агентов с очисткой удаленных серверов - -### Архитектура: -``` -[Master Server] ──SSH──┐ - ├── [Agent Server 1] - ├── [Agent Server 2] - ├── [Agent Server 3] - └── [Agent Server N] -``` - -## 🚀 Быстрый старт - -### 1. Настройка мастер-сервера - -Убедитесь что в `config/config.yaml` указано: -```yaml -cluster: - cluster_name: "MyCompany-Security" - master_server: true - ssh_timeout: 30 - ssh_retries: 3 -``` - -### 2. Подготовка SSH доступа - -#### Вариант A: SSH ключи (рекомендуется) -```bash -# Генерация ключей -ssh-keygen -t rsa -b 4096 -f ~/.ssh/pyguardian_cluster - -# Копирование на целевой сервер -ssh-copy-id -i ~/.ssh/pyguardian_cluster.pub root@192.168.1.100 -``` - -#### Вариант B: Пароли (менее безопасно) -Используется для первоначальной настройки или тестирования. - -### 3. Добавление серверов - -``` -/add_server web-01 192.168.1.100 -/add_server web-02 192.168.1.101 ubuntu 2222 -/add_server db-01 192.168.1.200 -``` - -### 4. Развертывание агентов - -``` -/deploy_agent web-01-192-168-1-100 -/deploy_agent web-02-192-168-1-101 -/deploy_agent db-01-192-168-1-200 -``` - -### 5. Мониторинг кластера - -``` -/cluster # Общая информация -/agents # Список агентов -/check_agents # Проверка статуса -``` - -## 📋 Команды управления кластером - -### Основные команды - -| Команда | Описание | Пример | -|---------|----------|--------| -| `/cluster` | Информация о кластере | `/cluster` | -| `/agents` | Список всех агентов | `/agents` | -| `/add_server` | Добавить сервер | `/add_server web-01 192.168.1.100` | -| `/remove_server` | Удалить сервер | `/remove_server web-01-192-168-1-100` | -| `/deploy_agent` | Развернуть агент | `/deploy_agent web-01-192-168-1-100` | -| `/check_agents` | Проверить статусы | `/check_agents` | - -### Детальные примеры - -#### Добавление сервера -``` -# Базовое добавление (root, порт 22) -/add_server web-server 192.168.1.100 - -# С кастомным пользователем -/add_server app-server 10.0.0.50 ubuntu - -# С кастомным портом -/add_server db-server 192.168.1.200 postgres 2222 - -# Полная форма -/add_server api-server 172.16.0.100 deploy 2200 -``` - -#### Развертывание агента -``` -# Обычное развертывание -/deploy_agent web-server-192-168-1-100 - -# Принудительная переустановка -/deploy_agent web-server-192-168-1-100 force -``` - -#### Удаление сервера -``` -# Простое удаление (агент остается) -/remove_server web-server-192-168-1-100 - -# С очисткой удаленного сервера -/remove_server web-server-192-168-1-100 cleanup -``` - -## 🔧 Конфигурация - -### Настройки кластера в config/config.yaml - -```yaml -cluster: - cluster_name: "Production-Cluster" # Название кластера - master_server: true # Мастер-сервер - agents_config_path: "/var/lib/pyguardian/agents.yaml" - deployment_path: "/opt/pyguardian" - ssh_timeout: 30 # Таймаут SSH (секунды) - ssh_retries: 3 # Попытки подключения - -# SSH ключи по умолчанию (опционально) -ssh: - default_key_path: "/root/.ssh/pyguardian_cluster" - default_user: "root" - default_port: 22 -``` - -### Файл агентов agents.yaml - -```yaml -cluster: - name: "Production-Cluster" - master_server: true - last_updated: "2024-11-25T15:30:00" - -agents: - web-01-192-168-1-100: - hostname: "web-01" - ip_address: "192.168.1.100" - ssh_port: 22 - ssh_user: "root" - ssh_key_path: "/root/.ssh/pyguardian_cluster" - status: "online" - last_check: "2024-11-25T15:25:00" - version: "1.0.0" - - api-server-172-16-0-100: - hostname: "api-server" - ip_address: "172.16.0.100" - ssh_port: 2200 - ssh_user: "deploy" - status: "deployed" - last_check: null - version: null -``` - -## 🛡️ Безопасность кластера - -### Рекомендации по безопасности: - -1. **SSH ключи**: Всегда используйте SSH ключи вместо паролей -2. **Ограниченные права**: Создайте отдельного пользователя для развертывания -3. **Файрвол**: Ограничьте SSH доступ только с мастер-сервера -4. **Мониторинг**: Регулярно проверяйте статус агентов -5. **Обновления**: Следите за обновлениями PyGuardian - -### Настройка пользователя для развертывания: - -```bash -# На целевом сервере -useradd -m -s /bin/bash pyguardian-deploy -usermod -aG sudo pyguardian-deploy - -# Настройка sudoers -echo 'pyguardian-deploy ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/pyguardian-deploy - -# Копирование SSH ключа -mkdir /home/pyguardian-deploy/.ssh -cp /root/.ssh/authorized_keys /home/pyguardian-deploy/.ssh/ -chown -R pyguardian-deploy:pyguardian-deploy /home/pyguardian-deploy/.ssh -chmod 700 /home/pyguardian-deploy/.ssh -chmod 600 /home/pyguardian-deploy/.ssh/authorized_keys -``` - -## 🚨 Устранение неполадок - -### Частые проблемы: - -#### Ошибка SSH соединения -``` -❌ Не удалось подключиться к серверу: Connection refused -``` - -**Решение:** -1. Проверьте доступность сервера: `ping 192.168.1.100` -2. Проверьте SSH сервис: `ssh root@192.168.1.100` -3. Проверьте порт SSH: `nmap -p 22 192.168.1.100` - -#### Ошибка прав доступа -``` -❌ Ошибка установки: Permission denied -``` - -**Решение:** -1. Убедитесь что пользователь имеет права sudo -2. Проверьте настройки sudoers -3. Попробуйте от root пользователя - -#### Агент не запускается -``` -🔴 service_status: failed -``` - -**Решение:** -1. Проверьте логи: `journalctl -u pyguardian-agent -f` -2. Проверьте конфигурацию агента -3. Переустановите агент: `/deploy_agent agent-id force` - -### Команды диагностики: - -```bash -# На мастер-сервере -tail -f /var/log/pyguardian.log - -# На агенте -systemctl status pyguardian-agent -journalctl -u pyguardian-agent -f -cat /var/log/pyguardian-agent.log -``` - -## 📊 Мониторинг кластера - -### Telegram уведомления - -PyGuardian автоматически отправляет уведомления о: -- Добавлении новых агентов -- Успешном развертывании -- Изменении статуса агентов -- Ошибках подключения - -### Пример вывода команд: - -#### /cluster -``` -🏢 Кластер Production-Cluster - -📊 Статистика: - • Всего агентов: 5 - • Онлайн: 4 - • Оффлайн: 1 - • Развернуто: 5 - -🖥️ Агенты: -🟢 web-01 (192.168.1.100) -🟢 web-02 (192.168.1.101) -🔴 db-01 (192.168.1.200) -🟢 api-server (172.16.0.100) -🟢 cache-01 (10.0.0.50) - -🕐 Последнее обновление: 2024-11-25 15:30:45 -``` - -#### /check_agents -``` -🔍 Результаты проверки агентов - -📊 Статистика: - • Проверено: 5 - • Онлайн: 4 - • Оффлайн: 1 - • Ошибки: 0 - -📋 Детали: -🟢 web-01: active -🟢 web-02: active -🔴 db-01: inactive -🟢 api-server: active -🟢 cache-01: active - -🕐 Время проверки: 15:32:10 -``` - -## 🔄 Автоматизация - -### Скрипты автоматизации - -Создайте скрипты для автоматического управления кластером: - -```bash -#!/bin/bash -# auto-deploy.sh - Автоматическое развертывание на список серверов - -SERVERS=( - "web-01 192.168.1.100" - "web-02 192.168.1.101" - "api-01 172.16.0.100" -) - -for server in "${SERVERS[@]}"; do - hostname=$(echo $server | cut -d' ' -f1) - ip=$(echo $server | cut -d' ' -f2) - - echo "Добавляю $hostname ($ip)..." - # Здесь может быть API вызов или автоматизация через expect -done -``` - -### Интеграция с CI/CD - -PyGuardian кластер может быть интегрирован с CI/CD пайплайнами для автоматического развертывания защиты на новые серверы. - -## 📈 Масштабирование - -### Рекомендации по масштабированию: - -- **До 10 серверов**: Один мастер-сервер -- **10-50 серверов**: Мастер + резервный мастер -- **50+ серверов**: Распределенная архитектура - -### Мониторинг производительности: - -- Время отклика SSH соединений -- Использование ресурсов мастер-сервера -- Скорость развертывания агентов -- Частота проверки статуса - ---- - -*Данная документация покрывает основные возможности управления кластером PyGuardian. Для дополнительной помощи обращайтесь к основной документации проекта.* \ No newline at end of file diff --git a/.history/docs/cluster-management_20251125203709.md b/.history/docs/cluster-management_20251125203709.md deleted file mode 100644 index 3cd8ceb..0000000 --- a/.history/docs/cluster-management_20251125203709.md +++ /dev/null @@ -1,345 +0,0 @@ -# Управление кластером PyGuardian - -## 🏢 Централизованное развертывание агентов - -PyGuardian поддерживает централизованное управление кластером серверов через Telegram бот. Мастер-сервер может автоматически развертывать и управлять агентами на удаленных серверах. - -## 🎯 Возможности кластера - -### Основные функции: -- **Автоматическое развертывание**: Установка PyGuardian на удаленные серверы -- **Централизованное управление**: Контроль всех агентов через один Telegram бот -- **Мониторинг статуса**: Проверка состояния агентов в реальном времени -- **SSH интеграция**: Безопасное подключение через SSH ключи или пароли -- **Автоматическая очистка**: Удаление агентов с очисткой удаленных серверов - -### Архитектура: -``` -[Master Server] ──SSH──┐ - ├── [Agent Server 1] - ├── [Agent Server 2] - ├── [Agent Server 3] - └── [Agent Server N] -``` - -## 🚀 Быстрый старт - -### 1. Настройка мастер-сервера - -Убедитесь что в `config/config.yaml` указано: -```yaml -cluster: - cluster_name: "MyCompany-Security" - master_server: true - ssh_timeout: 30 - ssh_retries: 3 -``` - -### 2. Подготовка SSH доступа - -#### Вариант A: SSH ключи (рекомендуется) -```bash -# Генерация ключей -ssh-keygen -t rsa -b 4096 -f ~/.ssh/pyguardian_cluster - -# Копирование на целевой сервер -ssh-copy-id -i ~/.ssh/pyguardian_cluster.pub root@192.168.1.100 -``` - -#### Вариант B: Пароли (менее безопасно) -Используется для первоначальной настройки или тестирования. - -### 3. Добавление серверов - -``` -/add_server web-01 192.168.1.100 -/add_server web-02 192.168.1.101 ubuntu 2222 -/add_server db-01 192.168.1.200 -``` - -### 4. Развертывание агентов - -``` -/deploy_agent web-01-192-168-1-100 -/deploy_agent web-02-192-168-1-101 -/deploy_agent db-01-192-168-1-200 -``` - -### 5. Мониторинг кластера - -``` -/cluster # Общая информация -/agents # Список агентов -/check_agents # Проверка статуса -``` - -## 📋 Команды управления кластером - -### Основные команды - -| Команда | Описание | Пример | -|---------|----------|--------| -| `/cluster` | Информация о кластере | `/cluster` | -| `/agents` | Список всех агентов | `/agents` | -| `/add_server` | Добавить сервер | `/add_server web-01 192.168.1.100` | -| `/remove_server` | Удалить сервер | `/remove_server web-01-192-168-1-100` | -| `/deploy_agent` | Развернуть агент | `/deploy_agent web-01-192-168-1-100` | -| `/check_agents` | Проверить статусы | `/check_agents` | - -### Детальные примеры - -#### Добавление сервера -``` -# Базовое добавление (root, порт 22) -/add_server web-server 192.168.1.100 - -# С кастомным пользователем -/add_server app-server 10.0.0.50 ubuntu - -# С кастомным портом -/add_server db-server 192.168.1.200 postgres 2222 - -# Полная форма -/add_server api-server 172.16.0.100 deploy 2200 -``` - -#### Развертывание агента -``` -# Обычное развертывание -/deploy_agent web-server-192-168-1-100 - -# Принудительная переустановка -/deploy_agent web-server-192-168-1-100 force -``` - -#### Удаление сервера -``` -# Простое удаление (агент остается) -/remove_server web-server-192-168-1-100 - -# С очисткой удаленного сервера -/remove_server web-server-192-168-1-100 cleanup -``` - -## 🔧 Конфигурация - -### Настройки кластера в config/config.yaml - -```yaml -cluster: - cluster_name: "Production-Cluster" # Название кластера - master_server: true # Мастер-сервер - agents_config_path: "/var/lib/pyguardian/agents.yaml" - deployment_path: "/opt/pyguardian" - ssh_timeout: 30 # Таймаут SSH (секунды) - ssh_retries: 3 # Попытки подключения - -# SSH ключи по умолчанию (опционально) -ssh: - default_key_path: "/root/.ssh/pyguardian_cluster" - default_user: "root" - default_port: 22 -``` - -### Файл агентов agents.yaml - -```yaml -cluster: - name: "Production-Cluster" - master_server: true - last_updated: "2024-11-25T15:30:00" - -agents: - web-01-192-168-1-100: - hostname: "web-01" - ip_address: "192.168.1.100" - ssh_port: 22 - ssh_user: "root" - ssh_key_path: "/root/.ssh/pyguardian_cluster" - status: "online" - last_check: "2024-11-25T15:25:00" - version: "1.0.0" - - api-server-172-16-0-100: - hostname: "api-server" - ip_address: "172.16.0.100" - ssh_port: 2200 - ssh_user: "deploy" - status: "deployed" - last_check: null - version: null -``` - -## 🛡️ Безопасность кластера - -### Рекомендации по безопасности: - -1. **SSH ключи**: Всегда используйте SSH ключи вместо паролей -2. **Ограниченные права**: Создайте отдельного пользователя для развертывания -3. **Файрвол**: Ограничьте SSH доступ только с мастер-сервера -4. **Мониторинг**: Регулярно проверяйте статус агентов -5. **Обновления**: Следите за обновлениями PyGuardian - -### Настройка пользователя для развертывания: - -```bash -# На целевом сервере -useradd -m -s /bin/bash pyguardian-deploy -usermod -aG sudo pyguardian-deploy - -# Настройка sudoers -echo 'pyguardian-deploy ALL=(ALL) NOPASSWD: ALL' > /etc/sudoers.d/pyguardian-deploy - -# Копирование SSH ключа -mkdir /home/pyguardian-deploy/.ssh -cp /root/.ssh/authorized_keys /home/pyguardian-deploy/.ssh/ -chown -R pyguardian-deploy:pyguardian-deploy /home/pyguardian-deploy/.ssh -chmod 700 /home/pyguardian-deploy/.ssh -chmod 600 /home/pyguardian-deploy/.ssh/authorized_keys -``` - -## 🚨 Устранение неполадок - -### Частые проблемы: - -#### Ошибка SSH соединения -``` -❌ Не удалось подключиться к серверу: Connection refused -``` - -**Решение:** -1. Проверьте доступность сервера: `ping 192.168.1.100` -2. Проверьте SSH сервис: `ssh root@192.168.1.100` -3. Проверьте порт SSH: `nmap -p 22 192.168.1.100` - -#### Ошибка прав доступа -``` -❌ Ошибка установки: Permission denied -``` - -**Решение:** -1. Убедитесь что пользователь имеет права sudo -2. Проверьте настройки sudoers -3. Попробуйте от root пользователя - -#### Агент не запускается -``` -🔴 service_status: failed -``` - -**Решение:** -1. Проверьте логи: `journalctl -u pyguardian-agent -f` -2. Проверьте конфигурацию агента -3. Переустановите агент: `/deploy_agent agent-id force` - -### Команды диагностики: - -```bash -# На мастер-сервере -tail -f /var/log/pyguardian.log - -# На агенте -systemctl status pyguardian-agent -journalctl -u pyguardian-agent -f -cat /var/log/pyguardian-agent.log -``` - -## 📊 Мониторинг кластера - -### Telegram уведомления - -PyGuardian автоматически отправляет уведомления о: -- Добавлении новых агентов -- Успешном развертывании -- Изменении статуса агентов -- Ошибках подключения - -### Пример вывода команд: - -#### /cluster -``` -🏢 Кластер Production-Cluster - -📊 Статистика: - • Всего агентов: 5 - • Онлайн: 4 - • Оффлайн: 1 - • Развернуто: 5 - -🖥️ Агенты: -🟢 web-01 (192.168.1.100) -🟢 web-02 (192.168.1.101) -🔴 db-01 (192.168.1.200) -🟢 api-server (172.16.0.100) -🟢 cache-01 (10.0.0.50) - -🕐 Последнее обновление: 2024-11-25 15:30:45 -``` - -#### /check_agents -``` -🔍 Результаты проверки агентов - -📊 Статистика: - • Проверено: 5 - • Онлайн: 4 - • Оффлайн: 1 - • Ошибки: 0 - -📋 Детали: -🟢 web-01: active -🟢 web-02: active -🔴 db-01: inactive -🟢 api-server: active -🟢 cache-01: active - -🕐 Время проверки: 15:32:10 -``` - -## 🔄 Автоматизация - -### Скрипты автоматизации - -Создайте скрипты для автоматического управления кластером: - -```bash -#!/bin/bash -# auto-deploy.sh - Автоматическое развертывание на список серверов - -SERVERS=( - "web-01 192.168.1.100" - "web-02 192.168.1.101" - "api-01 172.16.0.100" -) - -for server in "${SERVERS[@]}"; do - hostname=$(echo $server | cut -d' ' -f1) - ip=$(echo $server | cut -d' ' -f2) - - echo "Добавляю $hostname ($ip)..." - # Здесь может быть API вызов или автоматизация через expect -done -``` - -### Интеграция с CI/CD - -PyGuardian кластер может быть интегрирован с CI/CD пайплайнами для автоматического развертывания защиты на новые серверы. - -## 📈 Масштабирование - -### Рекомендации по масштабированию: - -- **До 10 серверов**: Один мастер-сервер -- **10-50 серверов**: Мастер + резервный мастер -- **50+ серверов**: Распределенная архитектура - -### Мониторинг производительности: - -- Время отклика SSH соединений -- Использование ресурсов мастер-сервера -- Скорость развертывания агентов -- Частота проверки статуса - ---- - -*Данная документация покрывает основные возможности управления кластером PyGuardian. Для дополнительной помощи обращайтесь к основной документации проекта.* \ No newline at end of file diff --git a/.history/examples/configurations_20251125204214.md b/.history/examples/configurations_20251125204214.md deleted file mode 100644 index d72fd49..0000000 --- a/.history/examples/configurations_20251125204214.md +++ /dev/null @@ -1,373 +0,0 @@ -# PyGuardian Configuration Examples -# Примеры конфигураций для различных режимов развертывания - -#========================================================================== -# 1. Standalone Configuration (Автономный режим) -# config/config.yaml для одиночного сервера -#========================================================================== - -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_users: [123456789] - log_channel: "@security_logs" - -security: - session_timeout: 30 - max_failed_attempts: 3 - ban_duration: 300 - enable_2fa: true - -firewall: - default_policy: "drop" - enable_ddos_protection: true - max_connections_per_ip: 10 - rate_limit: - ssh: 5 - http: 100 - https: 100 - -storage: - database_file: "data/pyguardian.db" - backup_interval: 3600 - log_retention_days: 30 - -monitoring: - check_interval: 60 - resource_alerts: - cpu_threshold: 80 - memory_threshold: 85 - disk_threshold: 90 - -#========================================================================== -# 2. Controller Configuration (Контроллер кластера) -# config/config.yaml для центрального управляющего узла -#========================================================================== - -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_users: [123456789] - log_channel: "@cluster_logs" - cluster_commands: true - -security: - session_timeout: 60 - max_failed_attempts: 5 - ban_duration: 600 - enable_2fa: true - cluster_auth_key: "your-cluster-secret-key" - -firewall: - default_policy: "drop" - enable_ddos_protection: true - max_connections_per_ip: 20 - rate_limit: - ssh: 10 - http: 200 - https: 200 - -storage: - database_file: "data/cluster_controller.db" - backup_interval: 1800 - log_retention_days: 60 - -monitoring: - check_interval: 30 - resource_alerts: - cpu_threshold: 70 - memory_threshold: 80 - disk_threshold: 85 - -cluster: - mode: "controller" - controller_host: "0.0.0.0" - controller_port: 8443 - api_secret: "your-api-secret-key" - agent_timeout: 120 - deployment: - ssh_key_path: "/root/.ssh/cluster_key" - default_user: "root" - installation_script: "/opt/pyguardian/scripts/install.sh" - notifications: - agent_offline_timeout: 300 - cluster_events: true - health_check_interval: 60 - -#========================================================================== -# 3. Agent Configuration (Агент кластера) -# config/config.yaml для управляемого узла -#========================================================================== - -telegram: - # Agent не имеет собственного бота, управляется контроллером - log_channel: "@agent_logs" - -security: - session_timeout: 30 - max_failed_attempts: 3 - ban_duration: 300 - enable_2fa: false - cluster_auth_key: "your-cluster-secret-key" - -firewall: - default_policy: "drop" - enable_ddos_protection: true - max_connections_per_ip: 10 - rate_limit: - ssh: 5 - http: 100 - https: 100 - -storage: - database_file: "data/agent.db" - backup_interval: 3600 - log_retention_days: 30 - -monitoring: - check_interval: 60 - resource_alerts: - cpu_threshold: 85 - memory_threshold: 90 - disk_threshold: 95 - -cluster: - mode: "agent" - controller_host: "YOUR_CONTROLLER_IP" - controller_port: 8443 - api_secret: "your-api-secret-key" - agent_id: "auto" # Автоматически сгенерируется - heartbeat_interval: 30 - report_interval: 60 - -#========================================================================== -# 4. Docker Compose Configuration -# docker-compose.yml для контейнеризированного развертывания -#========================================================================== - -version: '3.8' - -services: - pyguardian-controller: - build: . - container_name: pyguardian-controller - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - ./data:/opt/pyguardian/data - - ./config:/opt/pyguardian/config - - ./logs:/opt/pyguardian/logs - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=controller - command: ["python", "main.py", "--mode", "controller"] - - pyguardian-agent: - build: . - container_name: pyguardian-agent - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - ./data:/opt/pyguardian/data - - ./config:/opt/pyguardian/config - - ./logs:/opt/pyguardian/logs - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=agent - - CONTROLLER_HOST=your-controller-ip - command: ["python", "main.py", "--mode", "agent"] - depends_on: - - pyguardian-controller - -#========================================================================== -# 5. Systemd Service Templates -# /etc/systemd/system/pyguardian.service -#========================================================================== - -[Unit] -Description=PyGuardian Security System -After=network.target -Wants=network-online.target - -[Service] -Type=simple -User=pyguardian -Group=pyguardian -WorkingDirectory=/opt/pyguardian -ExecStart=/opt/pyguardian/venv/bin/python main.py -ExecReload=/bin/kill -HUP $MAINPID -Restart=always -RestartSec=30 -StandardOutput=journal -StandardError=journal -SyslogIdentifier=pyguardian - -# Security settings -NoNewPrivileges=yes -PrivateTmp=yes -ProtectSystem=strict -ProtectHome=yes -ReadWritePaths=/opt/pyguardian/data /opt/pyguardian/logs -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW - -[Install] -WantedBy=multi-user.target - -#========================================================================== -# 6. Nginx Proxy Configuration (для веб-интерфейса) -# /etc/nginx/sites-available/pyguardian -#========================================================================== - -server { - listen 80; - server_name your-domain.com; - return 301 https://$server_name$request_uri; -} - -server { - listen 443 ssl http2; - server_name your-domain.com; - - ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; - - location / { - proxy_pass http://127.0.0.1:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # WebSocket support - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - location /api/ { - proxy_pass http://127.0.0.1:8443/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} - -#========================================================================== -# 7. Environment Variables (.env файл) -#========================================================================== - -# PyGuardian Environment Variables -PYGUARDIAN_MODE=standalone -PYGUARDIAN_CONFIG=/opt/pyguardian/config/config.yaml -PYGUARDIAN_DATA_DIR=/opt/pyguardian/data -PYGUARDIAN_LOG_LEVEL=INFO - -# Telegram Configuration -TELEGRAM_BOT_TOKEN=your_bot_token_here -TELEGRAM_ADMIN_USERS=123456789,987654321 - -# Cluster Configuration (если используется) -CLUSTER_CONTROLLER_HOST=your-controller-ip -CLUSTER_CONTROLLER_PORT=8443 -CLUSTER_API_SECRET=your-api-secret -CLUSTER_AUTH_KEY=your-cluster-auth-key - -# Database Configuration -DATABASE_URL=sqlite:///opt/pyguardian/data/pyguardian.db - -# Security Settings -ENABLE_2FA=true -SESSION_TIMEOUT=30 -MAX_FAILED_ATTEMPTS=3 - -#========================================================================== -# 8. Firewall Rules Examples (iptables) -#========================================================================== - -#!/bin/bash -# PyGuardian Firewall Rules - -# Очистка существующих правил -iptables -F -iptables -X -iptables -Z - -# Политики по умолчанию -iptables -P INPUT DROP -iptables -P FORWARD DROP -iptables -P OUTPUT ACCEPT - -# Разрешить loopback -iptables -A INPUT -i lo -j ACCEPT -iptables -A OUTPUT -o lo -j ACCEPT - -# Разрешить установленные соединения -iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT - -# SSH (ограничить количество попыток) -iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH -iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP -iptables -A INPUT -p tcp --dport 22 -j ACCEPT - -# HTTP/HTTPS (с rate limiting) -iptables -A INPUT -p tcp --dport 80 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT -iptables -A INPUT -p tcp --dport 443 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT - -# Cluster API (только от контроллера) -iptables -A INPUT -p tcp --dport 8443 -s your-controller-ip -j ACCEPT - -# DDoS Protection -iptables -A INPUT -p tcp --syn -m limit --limit 1/s --limit-burst 3 -j ACCEPT -iptables -A INPUT -p icmp -m limit --limit 1/s --limit-burst 1 -j ACCEPT - -# Логирование отброшенных пакетов -iptables -A INPUT -j LOG --log-prefix "DROPPED: " --log-level 4 -iptables -A INPUT -j DROP - -#========================================================================== -# 9. Monitoring Configuration (для интеграции с Grafana/Prometheus) -#========================================================================== - -# prometheus.yml -global: - scrape_interval: 15s - -scrape_configs: - - job_name: 'pyguardian' - static_configs: - - targets: ['localhost:9090'] - metrics_path: /metrics - scrape_interval: 30s - - - job_name: 'pyguardian-cluster' - static_configs: - - targets: ['controller-ip:8443'] - metrics_path: /cluster/metrics - scrape_interval: 60s - -#========================================================================== -# 10. Backup Configuration -#========================================================================== - -#!/bin/bash -# PyGuardian Backup Script - -BACKUP_DIR="/opt/pyguardian/backups" -DATA_DIR="/opt/pyguardian/data" -CONFIG_DIR="/opt/pyguardian/config" -LOG_DIR="/opt/pyguardian/logs" - -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_FILE="pyguardian_backup_${DATE}.tar.gz" - -# Создать архив -tar -czf "${BACKUP_DIR}/${BACKUP_FILE}" \ - "${DATA_DIR}" \ - "${CONFIG_DIR}" \ - "${LOG_DIR}" - -# Оставить только последние 7 резервных копий -find "${BACKUP_DIR}" -name "pyguardian_backup_*.tar.gz" -type f -mtime +7 -delete - -echo "Backup completed: ${BACKUP_FILE}" \ No newline at end of file diff --git a/.history/examples/configurations_20251125204704.md b/.history/examples/configurations_20251125204704.md deleted file mode 100644 index d72fd49..0000000 --- a/.history/examples/configurations_20251125204704.md +++ /dev/null @@ -1,373 +0,0 @@ -# PyGuardian Configuration Examples -# Примеры конфигураций для различных режимов развертывания - -#========================================================================== -# 1. Standalone Configuration (Автономный режим) -# config/config.yaml для одиночного сервера -#========================================================================== - -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_users: [123456789] - log_channel: "@security_logs" - -security: - session_timeout: 30 - max_failed_attempts: 3 - ban_duration: 300 - enable_2fa: true - -firewall: - default_policy: "drop" - enable_ddos_protection: true - max_connections_per_ip: 10 - rate_limit: - ssh: 5 - http: 100 - https: 100 - -storage: - database_file: "data/pyguardian.db" - backup_interval: 3600 - log_retention_days: 30 - -monitoring: - check_interval: 60 - resource_alerts: - cpu_threshold: 80 - memory_threshold: 85 - disk_threshold: 90 - -#========================================================================== -# 2. Controller Configuration (Контроллер кластера) -# config/config.yaml для центрального управляющего узла -#========================================================================== - -telegram: - bot_token: "YOUR_BOT_TOKEN_HERE" - admin_users: [123456789] - log_channel: "@cluster_logs" - cluster_commands: true - -security: - session_timeout: 60 - max_failed_attempts: 5 - ban_duration: 600 - enable_2fa: true - cluster_auth_key: "your-cluster-secret-key" - -firewall: - default_policy: "drop" - enable_ddos_protection: true - max_connections_per_ip: 20 - rate_limit: - ssh: 10 - http: 200 - https: 200 - -storage: - database_file: "data/cluster_controller.db" - backup_interval: 1800 - log_retention_days: 60 - -monitoring: - check_interval: 30 - resource_alerts: - cpu_threshold: 70 - memory_threshold: 80 - disk_threshold: 85 - -cluster: - mode: "controller" - controller_host: "0.0.0.0" - controller_port: 8443 - api_secret: "your-api-secret-key" - agent_timeout: 120 - deployment: - ssh_key_path: "/root/.ssh/cluster_key" - default_user: "root" - installation_script: "/opt/pyguardian/scripts/install.sh" - notifications: - agent_offline_timeout: 300 - cluster_events: true - health_check_interval: 60 - -#========================================================================== -# 3. Agent Configuration (Агент кластера) -# config/config.yaml для управляемого узла -#========================================================================== - -telegram: - # Agent не имеет собственного бота, управляется контроллером - log_channel: "@agent_logs" - -security: - session_timeout: 30 - max_failed_attempts: 3 - ban_duration: 300 - enable_2fa: false - cluster_auth_key: "your-cluster-secret-key" - -firewall: - default_policy: "drop" - enable_ddos_protection: true - max_connections_per_ip: 10 - rate_limit: - ssh: 5 - http: 100 - https: 100 - -storage: - database_file: "data/agent.db" - backup_interval: 3600 - log_retention_days: 30 - -monitoring: - check_interval: 60 - resource_alerts: - cpu_threshold: 85 - memory_threshold: 90 - disk_threshold: 95 - -cluster: - mode: "agent" - controller_host: "YOUR_CONTROLLER_IP" - controller_port: 8443 - api_secret: "your-api-secret-key" - agent_id: "auto" # Автоматически сгенерируется - heartbeat_interval: 30 - report_interval: 60 - -#========================================================================== -# 4. Docker Compose Configuration -# docker-compose.yml для контейнеризированного развертывания -#========================================================================== - -version: '3.8' - -services: - pyguardian-controller: - build: . - container_name: pyguardian-controller - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - ./data:/opt/pyguardian/data - - ./config:/opt/pyguardian/config - - ./logs:/opt/pyguardian/logs - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=controller - command: ["python", "main.py", "--mode", "controller"] - - pyguardian-agent: - build: . - container_name: pyguardian-agent - restart: unless-stopped - privileged: true - network_mode: host - volumes: - - ./data:/opt/pyguardian/data - - ./config:/opt/pyguardian/config - - ./logs:/opt/pyguardian/logs - - /var/log:/var/log:ro - environment: - - PYGUARDIAN_MODE=agent - - CONTROLLER_HOST=your-controller-ip - command: ["python", "main.py", "--mode", "agent"] - depends_on: - - pyguardian-controller - -#========================================================================== -# 5. Systemd Service Templates -# /etc/systemd/system/pyguardian.service -#========================================================================== - -[Unit] -Description=PyGuardian Security System -After=network.target -Wants=network-online.target - -[Service] -Type=simple -User=pyguardian -Group=pyguardian -WorkingDirectory=/opt/pyguardian -ExecStart=/opt/pyguardian/venv/bin/python main.py -ExecReload=/bin/kill -HUP $MAINPID -Restart=always -RestartSec=30 -StandardOutput=journal -StandardError=journal -SyslogIdentifier=pyguardian - -# Security settings -NoNewPrivileges=yes -PrivateTmp=yes -ProtectSystem=strict -ProtectHome=yes -ReadWritePaths=/opt/pyguardian/data /opt/pyguardian/logs -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW - -[Install] -WantedBy=multi-user.target - -#========================================================================== -# 6. Nginx Proxy Configuration (для веб-интерфейса) -# /etc/nginx/sites-available/pyguardian -#========================================================================== - -server { - listen 80; - server_name your-domain.com; - return 301 https://$server_name$request_uri; -} - -server { - listen 443 ssl http2; - server_name your-domain.com; - - ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem; - - location / { - proxy_pass http://127.0.0.1:8080; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - - # WebSocket support - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } - - location /api/ { - proxy_pass http://127.0.0.1:8443/; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } -} - -#========================================================================== -# 7. Environment Variables (.env файл) -#========================================================================== - -# PyGuardian Environment Variables -PYGUARDIAN_MODE=standalone -PYGUARDIAN_CONFIG=/opt/pyguardian/config/config.yaml -PYGUARDIAN_DATA_DIR=/opt/pyguardian/data -PYGUARDIAN_LOG_LEVEL=INFO - -# Telegram Configuration -TELEGRAM_BOT_TOKEN=your_bot_token_here -TELEGRAM_ADMIN_USERS=123456789,987654321 - -# Cluster Configuration (если используется) -CLUSTER_CONTROLLER_HOST=your-controller-ip -CLUSTER_CONTROLLER_PORT=8443 -CLUSTER_API_SECRET=your-api-secret -CLUSTER_AUTH_KEY=your-cluster-auth-key - -# Database Configuration -DATABASE_URL=sqlite:///opt/pyguardian/data/pyguardian.db - -# Security Settings -ENABLE_2FA=true -SESSION_TIMEOUT=30 -MAX_FAILED_ATTEMPTS=3 - -#========================================================================== -# 8. Firewall Rules Examples (iptables) -#========================================================================== - -#!/bin/bash -# PyGuardian Firewall Rules - -# Очистка существующих правил -iptables -F -iptables -X -iptables -Z - -# Политики по умолчанию -iptables -P INPUT DROP -iptables -P FORWARD DROP -iptables -P OUTPUT ACCEPT - -# Разрешить loopback -iptables -A INPUT -i lo -j ACCEPT -iptables -A OUTPUT -o lo -j ACCEPT - -# Разрешить установленные соединения -iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT - -# SSH (ограничить количество попыток) -iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH -iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP -iptables -A INPUT -p tcp --dport 22 -j ACCEPT - -# HTTP/HTTPS (с rate limiting) -iptables -A INPUT -p tcp --dport 80 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT -iptables -A INPUT -p tcp --dport 443 -m limit --limit 25/minute --limit-burst 100 -j ACCEPT - -# Cluster API (только от контроллера) -iptables -A INPUT -p tcp --dport 8443 -s your-controller-ip -j ACCEPT - -# DDoS Protection -iptables -A INPUT -p tcp --syn -m limit --limit 1/s --limit-burst 3 -j ACCEPT -iptables -A INPUT -p icmp -m limit --limit 1/s --limit-burst 1 -j ACCEPT - -# Логирование отброшенных пакетов -iptables -A INPUT -j LOG --log-prefix "DROPPED: " --log-level 4 -iptables -A INPUT -j DROP - -#========================================================================== -# 9. Monitoring Configuration (для интеграции с Grafana/Prometheus) -#========================================================================== - -# prometheus.yml -global: - scrape_interval: 15s - -scrape_configs: - - job_name: 'pyguardian' - static_configs: - - targets: ['localhost:9090'] - metrics_path: /metrics - scrape_interval: 30s - - - job_name: 'pyguardian-cluster' - static_configs: - - targets: ['controller-ip:8443'] - metrics_path: /cluster/metrics - scrape_interval: 60s - -#========================================================================== -# 10. Backup Configuration -#========================================================================== - -#!/bin/bash -# PyGuardian Backup Script - -BACKUP_DIR="/opt/pyguardian/backups" -DATA_DIR="/opt/pyguardian/data" -CONFIG_DIR="/opt/pyguardian/config" -LOG_DIR="/opt/pyguardian/logs" - -DATE=$(date +%Y%m%d_%H%M%S) -BACKUP_FILE="pyguardian_backup_${DATE}.tar.gz" - -# Создать архив -tar -czf "${BACKUP_DIR}/${BACKUP_FILE}" \ - "${DATA_DIR}" \ - "${CONFIG_DIR}" \ - "${LOG_DIR}" - -# Оставить только последние 7 резервных копий -find "${BACKUP_DIR}" -name "pyguardian_backup_*.tar.gz" -type f -mtime +7 -delete - -echo "Backup completed: ${BACKUP_FILE}" \ No newline at end of file diff --git a/.history/examples/telegram-commands_20251125204304.md b/.history/examples/telegram-commands_20251125204304.md deleted file mode 100644 index ca1c583..0000000 --- a/.history/examples/telegram-commands_20251125204304.md +++ /dev/null @@ -1,396 +0,0 @@ -# PyGuardian Telegram Bot Commands Examples -# Примеры команд для управления системой через Telegram - -#========================================================================== -# 1. Основные команды безопасности -#========================================================================== - -# Получить статус системы -/status - -# Заблокировать IP адрес -/block 192.168.1.100 - -# Разблокировать IP адрес -/unblock 192.168.1.100 - -# Показать активные сессии -/sessions - -# Завершить сессию пользователя -/kick username - -# Просмотреть логи безопасности -/logs security - -#========================================================================== -# 2. Команды управления кластером (только для контроллера) -#========================================================================== - -# Показать все агенты в кластере -/cluster status - -# Добавить новый сервер в кластер -/cluster add 192.168.1.50 root /path/to/ssh/key - -# Развернуть агента на сервере -/cluster deploy 192.168.1.50 - -# Проверить соединение с агентом -/cluster ping agent-001 - -# Обновить конфигурацию агента -/cluster update agent-001 - -# Перезапустить агент -/cluster restart agent-001 - -# Удалить агент из кластера -/cluster remove agent-001 - -# Показать логи агента -/cluster logs agent-001 - -#========================================================================== -# 3. Интерактивные диалоги -#========================================================================== - -# Пример диалога добавления сервера в кластер: - -👤 User: /cluster add - -🤖 Bot: -``` -🔗 Добавление нового сервера в кластер - -Введите IP адрес сервера: -``` - -👤 User: 192.168.1.50 - -🤖 Bot: -``` -IP адрес: 192.168.1.50 ✅ - -Введите имя пользователя для SSH подключения: -(по умолчанию: root) -``` - -👤 User: root - -🤖 Bot: -``` -Пользователь: root ✅ - -Выберите метод аутентификации: -[1] SSH ключ -[2] Пароль -[3] Отмена -``` - -👤 User: 1 - -🤖 Bot: -``` -Метод аутентификации: SSH ключ ✅ - -Отправьте файл приватного ключа или укажите путь к нему: -(например: /root/.ssh/id_rsa) -``` - -👤 User: /root/.ssh/cluster_key - -🤖 Bot: -``` -SSH ключ: /root/.ssh/cluster_key ✅ - -Подтвердите добавление сервера: -IP: 192.168.1.50 -Пользователь: root -SSH ключ: /root/.ssh/cluster_key - -[Подтвердить] [Отмена] -``` - -👤 User: Подтвердить - -🤖 Bot: -``` -⏳ Подключение к серверу 192.168.1.50... -✅ Соединение установлено -⏳ Установка PyGuardian Agent... -⏳ Настройка конфигурации... -⏳ Запуск службы... -✅ Агент успешно развернут! - -🆔 ID агента: agent-003 -📡 Статус: Онлайн -🕐 Время развертывания: 2 мин 34 сек -``` - -#========================================================================== -# 4. Уведомления и алерты -#========================================================================== - -# Уведомления безопасности: -``` -🚨 SECURITY ALERT - -🕐 Time: 2024-01-15 14:30:25 -🎯 Server: web-server-01 (192.168.1.10) -⚠️ Event: Multiple failed SSH login attempts -📍 Source: 203.0.113.5 -📊 Attempts: 5 failed attempts in 2 minutes - -Action taken: IP blocked for 5 minutes -``` - -# Уведомления кластера: -``` -🔄 CLUSTER EVENT - -🕐 Time: 2024-01-15 14:35:10 -🎯 Agent: database-server-02 (agent-005) -⚠️ Event: Agent disconnected -🔍 Reason: Network timeout - -Status: Attempting reconnection... -``` - -# Уведомления о ресурсах: -``` -📈 RESOURCE ALERT - -🕐 Time: 2024-01-15 14:40:15 -🎯 Server: app-server-03 (192.168.1.30) -⚠️ Event: High CPU usage -📊 Current: 87% (threshold: 80%) -⏱️ Duration: 5 minutes - -Recommendation: Check running processes -``` - -#========================================================================== -# 5. Команды мониторинга -#========================================================================== - -# Показать ресурсы системы -/resources - -# Показать статистику файрвола -/firewall stats - -# Показать активные подключения -/connections - -# Показать топ процессов -/processes - -# Проверить обновления системы -/updates - -# Показать информацию о дисках -/disk - -# Показать сетевую статистику -/network - -#========================================================================== -# 6. Команды управления файрволом -#========================================================================== - -# Показать правила файрвола -/firewall rules - -# Добавить правило файрвола -/firewall add tcp 80 allow - -# Удалить правило файрвола -/firewall remove tcp 80 - -# Временно отключить файрвол -/firewall disable - -# Включить файрвол -/firewall enable - -# Показать заблокированные IP -/firewall blocked - -#========================================================================== -# 7. Команды резервного копирования -#========================================================================== - -# Создать резервную копию -/backup create - -# Показать список резервных копий -/backup list - -# Восстановить из резервной копии -/backup restore backup-20240115.tar.gz - -# Настроить автоматическое резервное копирование -/backup schedule daily 03:00 - -#========================================================================== -# 8. Административные команды -#========================================================================== - -# Показать конфигурацию -/config show - -# Обновить конфигурацию -/config update telegram.admin_users [123456789, 987654321] - -# Перезагрузить конфигурацию -/config reload - -# Показать версию системы -/version - -# Обновить систему -/update system - -# Перезапустить службы -/restart services - -#========================================================================== -# 9. Команды отладки -#========================================================================== - -# Показать подробные логи -/debug logs - -# Проверить соединение с базой данных -/debug database - -# Тестировать уведомления -/debug notify - -# Показать состояние служб -/debug services - -# Экспортировать диагностическую информацию -/debug export - -#========================================================================== -# 10. Примеры массового управления кластером -#========================================================================== - -# Обновить все агенты в кластере -/cluster update all - -# Перезапустить все агенты -/cluster restart all - -# Показать сводку по всем агентам -/cluster summary - -# Выполнить команду на всех агентах -/cluster exec "systemctl status nginx" - -# Отправить конфигурацию на все агенты -/cluster config push firewall.yaml - -# Собрать логи со всех агентов -/cluster logs collect - -#========================================================================== -# 11. Интеграция с внешними системами -#========================================================================== - -# Отправить метрики в Grafana -/metrics export grafana - -# Синхронизировать с SIEM системой -/siem sync - -# Обновить базу IP репутации -/reputation update - -# Отправить отчет по email -/report email weekly - -#========================================================================== -# 12. Примеры автоматических ответов -#========================================================================== - -# При попытке несанкционированного доступа: -``` -🚨 INTRUSION DETECTED - -An unauthorized access attempt has been detected and blocked automatically. - -🔹 Details: -• Source IP: 203.0.113.42 -• Target: ssh://server-01:22 -• Time: 2024-01-15 15:45:30 -• Action: IP blocked for 1 hour - -🔹 Recommendations: -• Review SSH access policies -• Consider IP whitelisting -• Enable 2FA for critical accounts - -Type /unblock 203.0.113.42 to manually unblock if needed -``` - -# При превышении ресурсов: -``` -⚠️ RESOURCE WARNING - -High resource usage detected on multiple servers. - -🔹 Affected servers: -• web-01: CPU 85% 📈 -• db-01: Memory 92% 🧠 -• app-01: Disk 88% 💾 - -🔹 Auto-scaling: -• Cluster load balancer activated -• Additional instances provisioning... -• ETA: 3 minutes - -Type /resources for detailed information -``` - -#========================================================================== -# 13. Кастомные команды через плагины -#========================================================================== - -# Wordpress специфичные команды -/wp update plugins -/wp backup database -/wp security scan - -# Docker управление -/docker ps -/docker restart container_name -/docker logs container_name - -# Nginx управление -/nginx reload -/nginx test -/nginx status - -# SSL сертификаты -/ssl check domain.com -/ssl renew all -/ssl notify expiring - -#========================================================================== -# 14. Голосовые команды (если поддерживается) -#========================================================================== - -# Примеры голосовых сообщений: -🎤 "Заблокировать IP 192.168.1.100" -🎤 "Показать статус кластера" -🎤 "Перезапустить все службы" -🎤 "Создать резервную копию" - -# Ответы голосом: -🔊 "IP адрес заблокирован" -🔊 "Все службы кластера работают нормально" -🔊 "Перезапуск служб завершен успешно" -🔊 "Резервная копия создана" \ No newline at end of file diff --git a/.history/examples/telegram-commands_20251125204704.md b/.history/examples/telegram-commands_20251125204704.md deleted file mode 100644 index ca1c583..0000000 --- a/.history/examples/telegram-commands_20251125204704.md +++ /dev/null @@ -1,396 +0,0 @@ -# PyGuardian Telegram Bot Commands Examples -# Примеры команд для управления системой через Telegram - -#========================================================================== -# 1. Основные команды безопасности -#========================================================================== - -# Получить статус системы -/status - -# Заблокировать IP адрес -/block 192.168.1.100 - -# Разблокировать IP адрес -/unblock 192.168.1.100 - -# Показать активные сессии -/sessions - -# Завершить сессию пользователя -/kick username - -# Просмотреть логи безопасности -/logs security - -#========================================================================== -# 2. Команды управления кластером (только для контроллера) -#========================================================================== - -# Показать все агенты в кластере -/cluster status - -# Добавить новый сервер в кластер -/cluster add 192.168.1.50 root /path/to/ssh/key - -# Развернуть агента на сервере -/cluster deploy 192.168.1.50 - -# Проверить соединение с агентом -/cluster ping agent-001 - -# Обновить конфигурацию агента -/cluster update agent-001 - -# Перезапустить агент -/cluster restart agent-001 - -# Удалить агент из кластера -/cluster remove agent-001 - -# Показать логи агента -/cluster logs agent-001 - -#========================================================================== -# 3. Интерактивные диалоги -#========================================================================== - -# Пример диалога добавления сервера в кластер: - -👤 User: /cluster add - -🤖 Bot: -``` -🔗 Добавление нового сервера в кластер - -Введите IP адрес сервера: -``` - -👤 User: 192.168.1.50 - -🤖 Bot: -``` -IP адрес: 192.168.1.50 ✅ - -Введите имя пользователя для SSH подключения: -(по умолчанию: root) -``` - -👤 User: root - -🤖 Bot: -``` -Пользователь: root ✅ - -Выберите метод аутентификации: -[1] SSH ключ -[2] Пароль -[3] Отмена -``` - -👤 User: 1 - -🤖 Bot: -``` -Метод аутентификации: SSH ключ ✅ - -Отправьте файл приватного ключа или укажите путь к нему: -(например: /root/.ssh/id_rsa) -``` - -👤 User: /root/.ssh/cluster_key - -🤖 Bot: -``` -SSH ключ: /root/.ssh/cluster_key ✅ - -Подтвердите добавление сервера: -IP: 192.168.1.50 -Пользователь: root -SSH ключ: /root/.ssh/cluster_key - -[Подтвердить] [Отмена] -``` - -👤 User: Подтвердить - -🤖 Bot: -``` -⏳ Подключение к серверу 192.168.1.50... -✅ Соединение установлено -⏳ Установка PyGuardian Agent... -⏳ Настройка конфигурации... -⏳ Запуск службы... -✅ Агент успешно развернут! - -🆔 ID агента: agent-003 -📡 Статус: Онлайн -🕐 Время развертывания: 2 мин 34 сек -``` - -#========================================================================== -# 4. Уведомления и алерты -#========================================================================== - -# Уведомления безопасности: -``` -🚨 SECURITY ALERT - -🕐 Time: 2024-01-15 14:30:25 -🎯 Server: web-server-01 (192.168.1.10) -⚠️ Event: Multiple failed SSH login attempts -📍 Source: 203.0.113.5 -📊 Attempts: 5 failed attempts in 2 minutes - -Action taken: IP blocked for 5 minutes -``` - -# Уведомления кластера: -``` -🔄 CLUSTER EVENT - -🕐 Time: 2024-01-15 14:35:10 -🎯 Agent: database-server-02 (agent-005) -⚠️ Event: Agent disconnected -🔍 Reason: Network timeout - -Status: Attempting reconnection... -``` - -# Уведомления о ресурсах: -``` -📈 RESOURCE ALERT - -🕐 Time: 2024-01-15 14:40:15 -🎯 Server: app-server-03 (192.168.1.30) -⚠️ Event: High CPU usage -📊 Current: 87% (threshold: 80%) -⏱️ Duration: 5 minutes - -Recommendation: Check running processes -``` - -#========================================================================== -# 5. Команды мониторинга -#========================================================================== - -# Показать ресурсы системы -/resources - -# Показать статистику файрвола -/firewall stats - -# Показать активные подключения -/connections - -# Показать топ процессов -/processes - -# Проверить обновления системы -/updates - -# Показать информацию о дисках -/disk - -# Показать сетевую статистику -/network - -#========================================================================== -# 6. Команды управления файрволом -#========================================================================== - -# Показать правила файрвола -/firewall rules - -# Добавить правило файрвола -/firewall add tcp 80 allow - -# Удалить правило файрвола -/firewall remove tcp 80 - -# Временно отключить файрвол -/firewall disable - -# Включить файрвол -/firewall enable - -# Показать заблокированные IP -/firewall blocked - -#========================================================================== -# 7. Команды резервного копирования -#========================================================================== - -# Создать резервную копию -/backup create - -# Показать список резервных копий -/backup list - -# Восстановить из резервной копии -/backup restore backup-20240115.tar.gz - -# Настроить автоматическое резервное копирование -/backup schedule daily 03:00 - -#========================================================================== -# 8. Административные команды -#========================================================================== - -# Показать конфигурацию -/config show - -# Обновить конфигурацию -/config update telegram.admin_users [123456789, 987654321] - -# Перезагрузить конфигурацию -/config reload - -# Показать версию системы -/version - -# Обновить систему -/update system - -# Перезапустить службы -/restart services - -#========================================================================== -# 9. Команды отладки -#========================================================================== - -# Показать подробные логи -/debug logs - -# Проверить соединение с базой данных -/debug database - -# Тестировать уведомления -/debug notify - -# Показать состояние служб -/debug services - -# Экспортировать диагностическую информацию -/debug export - -#========================================================================== -# 10. Примеры массового управления кластером -#========================================================================== - -# Обновить все агенты в кластере -/cluster update all - -# Перезапустить все агенты -/cluster restart all - -# Показать сводку по всем агентам -/cluster summary - -# Выполнить команду на всех агентах -/cluster exec "systemctl status nginx" - -# Отправить конфигурацию на все агенты -/cluster config push firewall.yaml - -# Собрать логи со всех агентов -/cluster logs collect - -#========================================================================== -# 11. Интеграция с внешними системами -#========================================================================== - -# Отправить метрики в Grafana -/metrics export grafana - -# Синхронизировать с SIEM системой -/siem sync - -# Обновить базу IP репутации -/reputation update - -# Отправить отчет по email -/report email weekly - -#========================================================================== -# 12. Примеры автоматических ответов -#========================================================================== - -# При попытке несанкционированного доступа: -``` -🚨 INTRUSION DETECTED - -An unauthorized access attempt has been detected and blocked automatically. - -🔹 Details: -• Source IP: 203.0.113.42 -• Target: ssh://server-01:22 -• Time: 2024-01-15 15:45:30 -• Action: IP blocked for 1 hour - -🔹 Recommendations: -• Review SSH access policies -• Consider IP whitelisting -• Enable 2FA for critical accounts - -Type /unblock 203.0.113.42 to manually unblock if needed -``` - -# При превышении ресурсов: -``` -⚠️ RESOURCE WARNING - -High resource usage detected on multiple servers. - -🔹 Affected servers: -• web-01: CPU 85% 📈 -• db-01: Memory 92% 🧠 -• app-01: Disk 88% 💾 - -🔹 Auto-scaling: -• Cluster load balancer activated -• Additional instances provisioning... -• ETA: 3 minutes - -Type /resources for detailed information -``` - -#========================================================================== -# 13. Кастомные команды через плагины -#========================================================================== - -# Wordpress специфичные команды -/wp update plugins -/wp backup database -/wp security scan - -# Docker управление -/docker ps -/docker restart container_name -/docker logs container_name - -# Nginx управление -/nginx reload -/nginx test -/nginx status - -# SSL сертификаты -/ssl check domain.com -/ssl renew all -/ssl notify expiring - -#========================================================================== -# 14. Голосовые команды (если поддерживается) -#========================================================================== - -# Примеры голосовых сообщений: -🎤 "Заблокировать IP 192.168.1.100" -🎤 "Показать статус кластера" -🎤 "Перезапустить все службы" -🎤 "Создать резервную копию" - -# Ответы голосом: -🔊 "IP адрес заблокирован" -🔊 "Все службы кластера работают нормально" -🔊 "Перезапуск служб завершен успешно" -🔊 "Резервная копия создана" \ No newline at end of file diff --git a/.history/install-new_20251125203857.sh b/.history/install-new_20251125203857.sh deleted file mode 100644 index c797d77..0000000 --- a/.history/install-new_20251125203857.sh +++ /dev/null @@ -1,195 +0,0 @@ -#!/bin/bash - -#========================================================================== -# PyGuardian Quick Installation Script -# Wrapper for the main installation system -# Author: SmartSolTech Team -# Version: 2.0 -#========================================================================== - -set -e - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -NC='\033[0m' - -print_header() { - echo -e "${BLUE}" - echo "=================================================" - echo " PyGuardian Security System - Quick Installer" - echo "=================================================" - echo -e "${NC}" -} - -print_info() { - echo -e "${BLUE}ℹ $1${NC}" -} - -print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -print_error() { - echo -e "${RED}✗ $1${NC}" -} - -# Check if running as root -check_root() { - if [[ $EUID -ne 0 ]]; then - print_error "This script must be run as root or with sudo" - echo "Usage: sudo ./install.sh" - exit 1 - fi -} - -# Show installation options -show_options() { - echo "" - print_info "Выберите тип установки:" - echo "" - echo "1) 🔧 Быстрая установка (Standalone режим)" - echo "2) 📋 Интерактивная установка с выбором режима" - echo "3) 🐳 Docker установка" - echo "4) 📖 Показать документацию" - echo "5) ❌ Выход" - echo "" -} - -# Quick standalone installation -quick_install() { - print_info "Запуск быстрой установки (Standalone режим)..." - - # Run the main installation script - if [[ -f "scripts/install.sh" ]]; then - chmod +x scripts/install.sh - ./scripts/install.sh --mode=standalone - else - print_error "Installation script not found!" - exit 1 - fi -} - -# Interactive installation -interactive_install() { - print_info "Запуск интерактивной установки..." - - if [[ -f "scripts/install.sh" ]]; then - chmod +x scripts/install.sh - ./scripts/install.sh - else - print_error "Installation script not found!" - exit 1 - fi -} - -# Docker installation -docker_install() { - print_info "Запуск Docker установки..." - - if [[ -f "scripts/docker-install.sh" ]]; then - chmod +x scripts/docker-install.sh - ./scripts/docker-install.sh - else - print_error "Docker installation script not found!" - exit 1 - fi -} - -# Show documentation -show_documentation() { - echo "" - echo -e "${BLUE}📖 Документация PyGuardian${NC}" - echo "" - echo "Основные файлы документации:" - echo " • README.md - Основная документация" - echo " • docs/CLUSTER_SETUP.md - Настройка кластера" - echo " • config/config.yaml - Пример конфигурации" - echo "" - echo "Онлайн ресурсы:" - echo " • GitHub: https://github.com/your-repo/PyGuardian" - echo " • Wiki: https://github.com/your-repo/PyGuardian/wiki" - echo "" - echo "Быстрые команды после установки:" - echo " • make install - Интерактивная установка" - echo " • make standalone - Автономный сервер" - echo " • make controller - Контроллер кластера" - echo " • make agent - Агент кластера" - echo "" -} - -# Main function -main() { - print_header - - # Check if running as root - check_root - - # If arguments provided, run directly - if [[ $# -gt 0 ]]; then - case "$1" in - --quick|--standalone) - quick_install - ;; - --interactive) - interactive_install - ;; - --docker) - docker_install - ;; - --help|-h) - echo "Usage: $0 [--quick|--interactive|--docker|--help]" - echo "" - echo "Options:" - echo " --quick Quick standalone installation" - echo " --interactive Interactive installation with mode selection" - echo " --docker Docker-based installation" - echo " --help Show this help" - ;; - *) - print_error "Unknown option: $1" - echo "Use --help for usage information" - exit 1 - ;; - esac - return - fi - - # Interactive menu - while true; do - show_options - read -p "Выберите опцию (1-5): " choice - - case $choice in - 1) - quick_install - break - ;; - 2) - interactive_install - break - ;; - 3) - docker_install - break - ;; - 4) - show_documentation - read -p "Нажмите Enter для продолжения..." - ;; - 5) - print_info "Выход из установщика" - exit 0 - ;; - *) - print_error "Неверный выбор. Введите число от 1 до 5." - ;; - esac - done - - print_success "Установка завершена! Спасибо за использование PyGuardian!" -} - -# Run main with all arguments -main "$@" \ No newline at end of file diff --git a/.history/install_20251125195202.sh b/.history/install_20251125195202.sh deleted file mode 100644 index 7fd9aea..0000000 --- a/.history/install_20251125195202.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash - -# PyGuardian Installation Script -# ============================== - -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_FILE="/etc/systemd/system/pyguardian.service" -CONFIG_DIR="/etc/pyguardian" -LOG_DIR="/var/log" -DATA_DIR="/var/lib/pyguardian" - -echo "🛡️ Установка PyGuardian - Linux Server Protection System" -echo "=========================================================" - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Этот скрипт должен быть запущен от root" - exit 1 -fi - -# Проверка Python 3.10+ -echo "📋 Проверка зависимостей..." -PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')") -REQUIRED_VERSION="3.10" - -if ! python3 -c "import sys; exit(0 if sys.version_info >= (3, 10) else 1)"; then - echo "❌ Требуется Python ${REQUIRED_VERSION}+ (найден ${PYTHON_VERSION})" - exit 1 -fi -echo "✅ Python ${PYTHON_VERSION} обнаружен" - -# Проверка pip -if ! command -v pip3 &> /dev/null; then - echo "❌ pip3 не найден. Установите python3-pip" - exit 1 -fi -echo "✅ pip3 найден" - -# Установка системных пакетов (опционально) -echo "📦 Установка системных зависимостей..." -if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv iptables -elif command -v yum &> /dev/null; then - yum install -y python3-pip python3-virtualenv iptables -elif command -v dnf &> /dev/null; then - dnf install -y python3-pip python3-virtualenv iptables -else - echo "⚠️ Автоматическая установка пакетов не поддерживается для этой системы" - echo " Убедитесь что установлены: python3-pip, iptables/nftables" -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p "$INSTALL_DIR" -mkdir -p "$CONFIG_DIR" -mkdir -p "$DATA_DIR" -chmod 700 "$DATA_DIR" - -# Копирование файлов -echo "📋 Копирование файлов..." -cp -r src/ "$INSTALL_DIR/" -cp main.py "$INSTALL_DIR/" -cp requirements.txt "$INSTALL_DIR/" - -# Копирование конфигурации -if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then - cp config/config.yaml "$CONFIG_DIR/" - echo "ℹ️ Конфигурация скопирована в $CONFIG_DIR/config.yaml" -else - echo "⚠️ Конфигурация уже существует в $CONFIG_DIR/config.yaml" -fi - -# Установка Python зависимостей -echo "🐍 Установка Python зависимостей..." -cd "$INSTALL_DIR" -pip3 install -r requirements.txt - -# Установка systemd сервиса -echo "⚙️ Установка systemd сервиса..." -sed "s|/opt/pyguardian|$INSTALL_DIR|g; s|/opt/pyguardian/config/config.yaml|$CONFIG_DIR/config.yaml|g" \ - systemd/pyguardian.service > "$SERVICE_FILE" - -# Права на файлы -chmod +x "$INSTALL_DIR/main.py" -chown -R root:root "$INSTALL_DIR" - -# Перезагрузка systemd -systemctl daemon-reload - -echo "" -echo "✅ PyGuardian успешно установлен!" -echo "" -echo "📝 Следующие шаги:" -echo "1. Настройте конфигурацию в $CONFIG_DIR/config.yaml" -echo "2. Получите токен Telegram бота от @BotFather" -echo "3. Узнайте ваш Telegram ID через @userinfobot" -echo "4. Обновите конфигурацию с токеном и ID" -echo "5. Запустите сервис: systemctl start pyguardian" -echo "6. Включите автозапуск: systemctl enable pyguardian" -echo "" -echo "🔧 Полезные команды:" -echo " systemctl status pyguardian - статус сервиса" -echo " systemctl logs pyguardian - просмотр логов" -echo " systemctl restart pyguardian - перезапуск" -echo "" -echo "📖 Документация: https://github.com/your-org/pyguardian" \ No newline at end of file diff --git a/.history/install_20251125202055.sh b/.history/install_20251125202055.sh deleted file mode 100644 index 7fd9aea..0000000 --- a/.history/install_20251125202055.sh +++ /dev/null @@ -1,109 +0,0 @@ -#!/bin/bash - -# PyGuardian Installation Script -# ============================== - -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_FILE="/etc/systemd/system/pyguardian.service" -CONFIG_DIR="/etc/pyguardian" -LOG_DIR="/var/log" -DATA_DIR="/var/lib/pyguardian" - -echo "🛡️ Установка PyGuardian - Linux Server Protection System" -echo "=========================================================" - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Этот скрипт должен быть запущен от root" - exit 1 -fi - -# Проверка Python 3.10+ -echo "📋 Проверка зависимостей..." -PYTHON_VERSION=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')") -REQUIRED_VERSION="3.10" - -if ! python3 -c "import sys; exit(0 if sys.version_info >= (3, 10) else 1)"; then - echo "❌ Требуется Python ${REQUIRED_VERSION}+ (найден ${PYTHON_VERSION})" - exit 1 -fi -echo "✅ Python ${PYTHON_VERSION} обнаружен" - -# Проверка pip -if ! command -v pip3 &> /dev/null; then - echo "❌ pip3 не найден. Установите python3-pip" - exit 1 -fi -echo "✅ pip3 найден" - -# Установка системных пакетов (опционально) -echo "📦 Установка системных зависимостей..." -if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv iptables -elif command -v yum &> /dev/null; then - yum install -y python3-pip python3-virtualenv iptables -elif command -v dnf &> /dev/null; then - dnf install -y python3-pip python3-virtualenv iptables -else - echo "⚠️ Автоматическая установка пакетов не поддерживается для этой системы" - echo " Убедитесь что установлены: python3-pip, iptables/nftables" -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p "$INSTALL_DIR" -mkdir -p "$CONFIG_DIR" -mkdir -p "$DATA_DIR" -chmod 700 "$DATA_DIR" - -# Копирование файлов -echo "📋 Копирование файлов..." -cp -r src/ "$INSTALL_DIR/" -cp main.py "$INSTALL_DIR/" -cp requirements.txt "$INSTALL_DIR/" - -# Копирование конфигурации -if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then - cp config/config.yaml "$CONFIG_DIR/" - echo "ℹ️ Конфигурация скопирована в $CONFIG_DIR/config.yaml" -else - echo "⚠️ Конфигурация уже существует в $CONFIG_DIR/config.yaml" -fi - -# Установка Python зависимостей -echo "🐍 Установка Python зависимостей..." -cd "$INSTALL_DIR" -pip3 install -r requirements.txt - -# Установка systemd сервиса -echo "⚙️ Установка systemd сервиса..." -sed "s|/opt/pyguardian|$INSTALL_DIR|g; s|/opt/pyguardian/config/config.yaml|$CONFIG_DIR/config.yaml|g" \ - systemd/pyguardian.service > "$SERVICE_FILE" - -# Права на файлы -chmod +x "$INSTALL_DIR/main.py" -chown -R root:root "$INSTALL_DIR" - -# Перезагрузка systemd -systemctl daemon-reload - -echo "" -echo "✅ PyGuardian успешно установлен!" -echo "" -echo "📝 Следующие шаги:" -echo "1. Настройте конфигурацию в $CONFIG_DIR/config.yaml" -echo "2. Получите токен Telegram бота от @BotFather" -echo "3. Узнайте ваш Telegram ID через @userinfobot" -echo "4. Обновите конфигурацию с токеном и ID" -echo "5. Запустите сервис: systemctl start pyguardian" -echo "6. Включите автозапуск: systemctl enable pyguardian" -echo "" -echo "🔧 Полезные команды:" -echo " systemctl status pyguardian - статус сервиса" -echo " systemctl logs pyguardian - просмотр логов" -echo " systemctl restart pyguardian - перезапуск" -echo "" -echo "📖 Документация: https://github.com/your-org/pyguardian" \ No newline at end of file diff --git a/.history/install_20251125203826.sh b/.history/install_20251125203826.sh deleted file mode 100644 index 1456fbc..0000000 --- a/.history/install_20251125203826.sh +++ /dev/null @@ -1,274 +0,0 @@ -#!/bin/bash - -#========================================================================== -# PyGuardian Quick Installation Script -# Wrapper for the main installation system -# Author: SmartSolTech Team -# Version: 2.0 -#========================================================================== - -set -e - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[1;33m' -NC='\033[0m' - -print_header() { - echo -e "${BLUE}" - echo "=================================================" - echo " PyGuardian Security System - Quick Installer" - echo "=================================================" - echo -e "${NC}" -} - -print_info() { - echo -e "${BLUE}ℹ $1${NC}" -} - -print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -print_error() { - echo -e "${RED}✗ $1${NC}" -} - -# Check if running as root -check_root() { - if [[ $EUID -ne 0 ]]; then - print_error "This script must be run as root or with sudo" - echo "Usage: sudo ./install.sh" - exit 1 - fi -} - -# Show installation options -show_options() { - echo "" - print_info "Выберите тип установки:" - echo "" - echo "1) 🔧 Быстрая установка (Standalone режим)" - echo "2) 📋 Интерактивная установка с выбором режима" - echo "3) 🐳 Docker установка" - echo "4) 📖 Показать документацию" - echo "5) ❌ Выход" - echo "" -} - -# Quick standalone installation -quick_install() { - print_info "Запуск быстрой установки (Standalone режим)..." - - # Run the main installation script - if [[ -f "scripts/install.sh" ]]; then - chmod +x scripts/install.sh - ./scripts/install.sh --mode=standalone - else - print_error "Installation script not found!" - exit 1 - fi -} - -# Interactive installation -interactive_install() { - print_info "Запуск интерактивной установки..." - - if [[ -f "scripts/install.sh" ]]; then - chmod +x scripts/install.sh - ./scripts/install.sh - else - print_error "Installation script not found!" - exit 1 - fi -} - -# Docker installation -docker_install() { - print_info "Запуск Docker установки..." - - if [[ -f "scripts/docker-install.sh" ]]; then - chmod +x scripts/docker-install.sh - ./scripts/docker-install.sh - else - print_error "Docker installation script not found!" - exit 1 - fi -} - -# Show documentation -show_documentation() { - echo "" - echo -e "${BLUE}📖 Документация PyGuardian${NC}" - echo "" - echo "Основные файлы документации:" - echo " • README.md - Основная документация" - echo " • docs/CLUSTER_SETUP.md - Настройка кластера" - echo " • config/config.yaml - Пример конфигурации" - echo "" - echo "Онлайн ресурсы:" - echo " • GitHub: https://github.com/your-repo/PyGuardian" - echo " • Wiki: https://github.com/your-repo/PyGuardian/wiki" - echo "" - echo "Быстрые команды после установки:" - echo " • make install - Интерактивная установка" - echo " • make standalone - Автономный сервер" - echo " • make controller - Контроллер кластера" - echo " • make agent - Агент кластера" - echo "" -} - -# Main function -main() { - print_header - - # Check if running as root - check_root - - # If arguments provided, run directly - if [[ $# -gt 0 ]]; then - case "$1" in - --quick|--standalone) - quick_install - ;; - --interactive) - interactive_install - ;; - --docker) - docker_install - ;; - --help|-h) - echo "Usage: $0 [--quick|--interactive|--docker|--help]" - echo "" - echo "Options:" - echo " --quick Quick standalone installation" - echo " --interactive Interactive installation with mode selection" - echo " --docker Docker-based installation" - echo " --help Show this help" - ;; - *) - print_error "Unknown option: $1" - echo "Use --help for usage information" - exit 1 - ;; - esac - return - fi - - # Interactive menu - while true; do - show_options - read -p "Выберите опцию (1-5): " choice - - case $choice in - 1) - quick_install - break - ;; - 2) - interactive_install - break - ;; - 3) - docker_install - break - ;; - 4) - show_documentation - read -p "Нажмите Enter для продолжения..." - ;; - 5) - print_info "Выход из установщика" - exit 0 - ;; - *) - print_error "Неверный выбор. Введите число от 1 до 5." - ;; - esac - done - - print_success "Установка завершена! Спасибо за использование PyGuardian!" -} - -# Run main with all arguments -main "$@" -echo "✅ Python ${PYTHON_VERSION} обнаружен" - -# Проверка pip -if ! command -v pip3 &> /dev/null; then - echo "❌ pip3 не найден. Установите python3-pip" - exit 1 -fi -echo "✅ pip3 найден" - -# Установка системных пакетов (опционально) -echo "📦 Установка системных зависимостей..." -if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv iptables -elif command -v yum &> /dev/null; then - yum install -y python3-pip python3-virtualenv iptables -elif command -v dnf &> /dev/null; then - dnf install -y python3-pip python3-virtualenv iptables -else - echo "⚠️ Автоматическая установка пакетов не поддерживается для этой системы" - echo " Убедитесь что установлены: python3-pip, iptables/nftables" -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p "$INSTALL_DIR" -mkdir -p "$CONFIG_DIR" -mkdir -p "$DATA_DIR" -chmod 700 "$DATA_DIR" - -# Копирование файлов -echo "📋 Копирование файлов..." -cp -r src/ "$INSTALL_DIR/" -cp main.py "$INSTALL_DIR/" -cp requirements.txt "$INSTALL_DIR/" - -# Копирование конфигурации -if [[ ! -f "$CONFIG_DIR/config.yaml" ]]; then - cp config/config.yaml "$CONFIG_DIR/" - echo "ℹ️ Конфигурация скопирована в $CONFIG_DIR/config.yaml" -else - echo "⚠️ Конфигурация уже существует в $CONFIG_DIR/config.yaml" -fi - -# Установка Python зависимостей -echo "🐍 Установка Python зависимостей..." -cd "$INSTALL_DIR" -pip3 install -r requirements.txt - -# Установка systemd сервиса -echo "⚙️ Установка systemd сервиса..." -sed "s|/opt/pyguardian|$INSTALL_DIR|g; s|/opt/pyguardian/config/config.yaml|$CONFIG_DIR/config.yaml|g" \ - systemd/pyguardian.service > "$SERVICE_FILE" - -# Права на файлы -chmod +x "$INSTALL_DIR/main.py" -chown -R root:root "$INSTALL_DIR" - -# Перезагрузка systemd -systemctl daemon-reload - -echo "" -echo "✅ PyGuardian успешно установлен!" -echo "" -echo "📝 Следующие шаги:" -echo "1. Настройте конфигурацию в $CONFIG_DIR/config.yaml" -echo "2. Получите токен Telegram бота от @BotFather" -echo "3. Узнайте ваш Telegram ID через @userinfobot" -echo "4. Обновите конфигурацию с токеном и ID" -echo "5. Запустите сервис: systemctl start pyguardian" -echo "6. Включите автозапуск: systemctl enable pyguardian" -echo "" -echo "🔧 Полезные команды:" -echo " systemctl status pyguardian - статус сервиса" -echo " systemctl logs pyguardian - просмотр логов" -echo " systemctl restart pyguardian - перезапуск" -echo "" -echo "📖 Документация: https://github.com/your-org/pyguardian" \ No newline at end of file diff --git a/.history/install_20251125210243.sh b/.history/install_20251125212605.sh similarity index 94% rename from .history/install_20251125210243.sh rename to .history/install_20251125212605.sh index 5571b1a..187b64e 100644 --- a/.history/install_20251125210243.sh +++ b/.history/install_20251125212605.sh @@ -279,8 +279,16 @@ main() { echo "" echo "Next steps:" echo "1. Configure your Telegram bot token in /opt/pyguardian/config/config.yaml" - echo "2. Start the service: systemctl start pyguardian" - echo "3. Enable auto-start: systemctl enable pyguardian" + echo "2. Review authentication settings in /opt/pyguardian/config/auth.yaml" + echo "3. Start the service: systemctl start pyguardian" + echo "4. Enable auto-start: systemctl enable pyguardian" + echo "" + echo -e "${YELLOW}[INFO]${NC} PyGuardian v2.1.0 includes:" + echo " ✓ JWT-based agent authentication" + echo " ✓ Encrypted agent credentials storage" + echo " ✓ RESTful API server with authentication" + echo " ✓ Comprehensive testing suite" + echo " ✓ CI/CD pipeline with security scanning" echo "" echo "Documentation: ${PYGUARDIAN_REPO}/tree/main/documentation" echo "Support: https://github.com/SmartSolTech/PyGuardian/issues" diff --git a/.history/install_20251125210433.sh b/.history/install_20251125212611.sh similarity index 94% rename from .history/install_20251125210433.sh rename to .history/install_20251125212611.sh index 5571b1a..6a4a97e 100644 --- a/.history/install_20251125210433.sh +++ b/.history/install_20251125212611.sh @@ -15,7 +15,7 @@ YELLOW='\033[1;33m' NC='\033[0m' # Project information -PYGUARDIAN_VERSION="2.0.0" +PYGUARDIAN_VERSION="2.1.0" PYGUARDIAN_REPO="https://github.com/SmartSolTech/PyGuardian" print_header() { @@ -279,8 +279,16 @@ main() { echo "" echo "Next steps:" echo "1. Configure your Telegram bot token in /opt/pyguardian/config/config.yaml" - echo "2. Start the service: systemctl start pyguardian" - echo "3. Enable auto-start: systemctl enable pyguardian" + echo "2. Review authentication settings in /opt/pyguardian/config/auth.yaml" + echo "3. Start the service: systemctl start pyguardian" + echo "4. Enable auto-start: systemctl enable pyguardian" + echo "" + echo -e "${YELLOW}[INFO]${NC} PyGuardian v2.1.0 includes:" + echo " ✓ JWT-based agent authentication" + echo " ✓ Encrypted agent credentials storage" + echo " ✓ RESTful API server with authentication" + echo " ✓ Comprehensive testing suite" + echo " ✓ CI/CD pipeline with security scanning" echo "" echo "Documentation: ${PYGUARDIAN_REPO}/tree/main/documentation" echo "Support: https://github.com/SmartSolTech/PyGuardian/issues" diff --git a/.history/install_20251125212626.sh b/.history/install_20251125212626.sh new file mode 100644 index 0000000..6a4a97e --- /dev/null +++ b/.history/install_20251125212626.sh @@ -0,0 +1,301 @@ +#!/bin/bash + +#========================================================================== +# PyGuardian Universal Installer +# Quick installation wrapper for all PyGuardian deployment modes +#========================================================================== + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +BLUE='\033[0;34m' +YELLOW='\033[1;33m' +NC='\033[0m' + +# Project information +PYGUARDIAN_VERSION="2.1.0" +PYGUARDIAN_REPO="https://github.com/SmartSolTech/PyGuardian" + +print_header() { + echo -e "${BLUE}" + echo "=================================================" + echo " PyGuardian Security System v${PYGUARDIAN_VERSION}" + echo " Universal Installation Wrapper" + echo "=================================================" + echo -e "${NC}" +} + +print_usage() { + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --mode MODE Installation mode (standalone|controller|agent)" + echo " --controller HOST Controller IP (required for agent mode)" + echo " --docker Use Docker installation" + echo " --help Show this help message" + echo "" + echo "Examples:" + echo " $0 # Interactive installation" + echo " $0 --mode standalone # Standalone installation" + echo " $0 --mode controller # Cluster controller" + echo " $0 --mode agent --controller 1.2.3.4 # Cluster agent" + echo " $0 --docker # Docker installation" +} + +check_system() { + echo -e "${BLUE}[INFO]${NC} Checking system requirements..." + + # Check if running as root + if [[ $EUID -ne 0 ]]; then + echo -e "${RED}[ERROR]${NC} This script must be run as root or with sudo" + exit 1 + fi + + # Check operating system + if ! command -v systemctl &> /dev/null; then + echo -e "${RED}[ERROR]${NC} This installer requires a systemd-based Linux distribution" + exit 1 + fi + + # Check Python version + if command -v python3 &> /dev/null; then + PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') + echo -e "${GREEN}[OK]${NC} Python ${PYTHON_VERSION} found" + + if ! python3 -c 'import sys; exit(0 if sys.version_info >= (3, 10) else 1)'; then + echo -e "${RED}[ERROR]${NC} Python 3.10+ is required (found ${PYTHON_VERSION})" + exit 1 + fi + else + echo -e "${RED}[ERROR]${NC} Python3 not found. Please install Python 3.10+" + exit 1 + fi + + echo -e "${GREEN}[OK]${NC} System requirements satisfied" +} + +download_installer() { + echo -e "${BLUE}[INFO]${NC} Downloading PyGuardian installer..." + + # Create temporary directory + TEMP_DIR=$(mktemp -d) + cd "$TEMP_DIR" + + # Download the detailed installer + if command -v curl &> /dev/null; then + curl -fsSL "${PYGUARDIAN_REPO}/raw/main/deployment/scripts/install.sh" -o install.sh + elif command -v wget &> /dev/null; then + wget -q "${PYGUARDIAN_REPO}/raw/main/deployment/scripts/install.sh" -O install.sh + else + echo -e "${RED}[ERROR]${NC} Neither curl nor wget found. Please install one of them." + exit 1 + fi + + chmod +x install.sh + echo -e "${GREEN}[OK]${NC} Installer downloaded to ${TEMP_DIR}/install.sh" + + # Export for use in main function + export INSTALLER_PATH="${TEMP_DIR}/install.sh" +} + +run_docker_installation() { + echo -e "${BLUE}[INFO]${NC} Starting Docker-based installation..." + + # Check if Docker is installed + if ! command -v docker &> /dev/null; then + echo -e "${YELLOW}[WARNING]${NC} Docker not found. Installing Docker..." + + # Install Docker + curl -fsSL https://get.docker.com -o get-docker.sh + sh get-docker.sh + + # Start Docker service + systemctl enable docker + systemctl start docker + + echo -e "${GREEN}[OK]${NC} Docker installed successfully" + fi + + # Check if docker-compose is available + if ! command -v docker-compose &> /dev/null; then + if ! docker compose version &> /dev/null; then + echo -e "${YELLOW}[WARNING]${NC} Docker Compose not found. Installing..." + + # Install docker-compose + curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + chmod +x /usr/local/bin/docker-compose + + echo -e "${GREEN}[OK]${NC} Docker Compose installed" + fi + fi + + # Download docker installation script + TEMP_DIR=$(mktemp -d) + cd "$TEMP_DIR" + + if command -v curl &> /dev/null; then + curl -fsSL "${PYGUARDIAN_REPO}/raw/main/deployment/scripts/docker-install.sh" -o docker-install.sh + else + wget -q "${PYGUARDIAN_REPO}/raw/main/deployment/scripts/docker-install.sh" -O docker-install.sh + fi + + chmod +x docker-install.sh + + # Run Docker installation + ./docker-install.sh "$@" +} + +run_interactive_installation() { + echo -e "${BLUE}[INFO]${NC} Starting interactive installation..." + echo "" + + echo "Select PyGuardian installation mode:" + echo "1) Standalone server (all components on one server)" + echo "2) Cluster controller (central management node)" + echo "3) Cluster agent (managed node)" + echo "4) Docker installation" + echo "" + + while true; do + read -p "Enter your choice (1-4): " choice + case $choice in + 1) + echo -e "${GREEN}[SELECTED]${NC} Standalone installation" + "$INSTALLER_PATH" --mode standalone + break + ;; + 2) + echo -e "${GREEN}[SELECTED]${NC} Cluster controller installation" + "$INSTALLER_PATH" --mode controller + break + ;; + 3) + echo -e "${GREEN}[SELECTED]${NC} Cluster agent installation" + echo "" + read -p "Enter controller IP address: " controller_ip + if [[ -z "$controller_ip" ]]; then + echo -e "${RED}[ERROR]${NC} Controller IP is required for agent mode" + continue + fi + "$INSTALLER_PATH" --mode agent --controller "$controller_ip" + break + ;; + 4) + echo -e "${GREEN}[SELECTED]${NC} Docker installation" + run_docker_installation + break + ;; + *) + echo -e "${RED}[ERROR]${NC} Invalid choice. Please select 1-4." + ;; + esac + done +} + +main() { + print_header + + # Parse command line arguments + MODE="" + CONTROLLER_HOST="" + USE_DOCKER=false + + while [[ $# -gt 0 ]]; do + case $1 in + --mode) + MODE="$2" + shift 2 + ;; + --controller) + CONTROLLER_HOST="$2" + shift 2 + ;; + --docker) + USE_DOCKER=true + shift + ;; + --help) + print_usage + exit 0 + ;; + *) + echo -e "${RED}[ERROR]${NC} Unknown option: $1" + print_usage + exit 1 + ;; + esac + done + + # System checks + check_system + + # Handle Docker installation + if [[ "$USE_DOCKER" == true ]]; then + run_docker_installation "$@" + exit 0 + fi + + # Download detailed installer + download_installer + + # Run installation based on mode + if [[ -n "$MODE" ]]; then + echo -e "${BLUE}[INFO]${NC} Running ${MODE} installation..." + + # Validate mode + if [[ "$MODE" != "standalone" && "$MODE" != "controller" && "$MODE" != "agent" ]]; then + echo -e "${RED}[ERROR]${NC} Invalid mode: $MODE" + print_usage + exit 1 + fi + + # Check controller host for agent mode + if [[ "$MODE" == "agent" && -z "$CONTROLLER_HOST" ]]; then + echo -e "${RED}[ERROR]${NC} Controller host is required for agent mode" + print_usage + exit 1 + fi + + # Run installer with specified mode + if [[ "$MODE" == "agent" ]]; then + "$INSTALLER_PATH" --mode agent --controller "$CONTROLLER_HOST" + else + "$INSTALLER_PATH" --mode "$MODE" + fi + else + # Interactive mode + run_interactive_installation + fi + + # Cleanup + if [[ -n "$INSTALLER_PATH" ]]; then + rm -rf "$(dirname "$INSTALLER_PATH")" + fi + + echo "" + echo -e "${GREEN}[SUCCESS]${NC} PyGuardian installation completed!" + echo "" + echo "Next steps:" + echo "1. Configure your Telegram bot token in /opt/pyguardian/config/config.yaml" + echo "2. Review authentication settings in /opt/pyguardian/config/auth.yaml" + echo "3. Start the service: systemctl start pyguardian" + echo "4. Enable auto-start: systemctl enable pyguardian" + echo "" + echo -e "${YELLOW}[INFO]${NC} PyGuardian v2.1.0 includes:" + echo " ✓ JWT-based agent authentication" + echo " ✓ Encrypted agent credentials storage" + echo " ✓ RESTful API server with authentication" + echo " ✓ Comprehensive testing suite" + echo " ✓ CI/CD pipeline with security scanning" + echo "" + echo "Documentation: ${PYGUARDIAN_REPO}/tree/main/documentation" + echo "Support: https://github.com/SmartSolTech/PyGuardian/issues" +} + +# Handle script errors +trap 'echo -e "${RED}[ERROR]${NC} Installation failed. Check logs above."; exit 1' ERR + +# Run main function +main "$@" \ No newline at end of file diff --git a/.history/install_agent_20251125203052.sh b/.history/install_agent_20251125203052.sh deleted file mode 100644 index edc057f..0000000 --- a/.history/install_agent_20251125203052.sh +++ /dev/null @@ -1,370 +0,0 @@ -#!/bin/bash - -# PyGuardian Agent Installation Script -# Usage: ./install_agent.sh --master --port - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Default values -MASTER_IP="" -MASTER_PORT="8080" -AGENT_PORT="8081" -INSTALL_DIR="/opt/pyguardian-agent" -SERVICE_NAME="pyguardian-agent" - -# Functions -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -show_usage() { - echo "PyGuardian Agent Installation Script" - echo "" - echo "Usage: $0 --master [OPTIONS]" - echo "" - echo "Required:" - echo " --master Master server IP address" - echo "" - echo "Optional:" - echo " --port Master server port (default: 8080)" - echo " --agent-port

Agent listen port (default: 8081)" - echo " --help Show this help" - echo "" - echo "Example:" - echo " $0 --master 192.168.1.100 --port 8080" -} - -# Parse command line arguments -while [[ $# -gt 0 ]]; do - case $1 in - --master) - MASTER_IP="$2" - shift 2 - ;; - --port) - MASTER_PORT="$2" - shift 2 - ;; - --agent-port) - AGENT_PORT="$2" - shift 2 - ;; - --help) - show_usage - exit 0 - ;; - *) - log_error "Unknown option: $1" - show_usage - exit 1 - ;; - esac -done - -# Validate required parameters -if [[ -z "$MASTER_IP" ]]; then - log_error "Master IP is required" - show_usage - exit 1 -fi - -# Check if running as root -if [[ $EUID -ne 0 ]]; then - log_error "This script must be run as root" - exit 1 -fi - -log_info "Starting PyGuardian Agent installation..." -log_info "Master Server: ${MASTER_IP}:${MASTER_PORT}" -log_info "Agent Port: ${AGENT_PORT}" - -# Check system requirements -log_info "Checking system requirements..." - -# Check Python version -if ! command -v python3 &> /dev/null; then - log_error "Python3 is required but not installed" - exit 1 -fi - -PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') -REQUIRED_VERSION="3.10" - -if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)" 2>/dev/null; then - log_error "Python 3.10+ is required. Found: $PYTHON_VERSION" - exit 1 -fi - -log_success "Python version check passed: $PYTHON_VERSION" - -# Install system dependencies -log_info "Installing system dependencies..." - -if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv curl wget -elif command -v yum &> /dev/null; then - yum install -y python3-pip curl wget -elif command -v dnf &> /dev/null; then - dnf install -y python3-pip curl wget -else - log_warning "Unknown package manager. Please install python3-pip manually." -fi - -# Create installation directory -log_info "Creating installation directory..." -mkdir -p "$INSTALL_DIR" -mkdir -p /etc/pyguardian-agent -mkdir -p /var/log/pyguardian-agent - -# Create agent configuration -log_info "Creating agent configuration..." -cat > /etc/pyguardian-agent/config.yaml << EOF -# PyGuardian Agent Configuration - -agent: - port: ${AGENT_PORT} - master_host: "${MASTER_IP}" - master_port: ${MASTER_PORT} - heartbeat_interval: 30 - max_reconnect_attempts: 10 - reconnect_delay: 5 - -logging: - level: INFO - file: /var/log/pyguardian-agent/agent.log - max_size: 10MB - backup_count: 5 - -security: - ssl_verify: true - api_key_file: /etc/pyguardian-agent/api.key -EOF - -# Create simple agent script -log_info "Creating agent script..." -cat > "$INSTALL_DIR/agent.py" << 'EOF' -#!/usr/bin/env python3 -""" -PyGuardian Agent -Lightweight agent for cluster management -""" - -import asyncio -import json -import logging -import signal -import sys -import time -import yaml -import aiohttp -import psutil -from pathlib import Path - -class PyGuardianAgent: - def __init__(self, config_path="/etc/pyguardian-agent/config.yaml"): - self.config_path = config_path - self.config = self.load_config() - self.setup_logging() - self.session = None - self.running = False - - def load_config(self): - """Load agent configuration""" - try: - with open(self.config_path, 'r') as f: - return yaml.safe_load(f) - except Exception as e: - print(f"Error loading config: {e}") - sys.exit(1) - - def setup_logging(self): - """Setup logging configuration""" - log_file = self.config.get('logging', {}).get('file', '/var/log/pyguardian-agent/agent.log') - log_level = getattr(logging, self.config.get('logging', {}).get('level', 'INFO')) - - # Create log directory if it doesn't exist - Path(log_file).parent.mkdir(parents=True, exist_ok=True) - - logging.basicConfig( - level=log_level, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[ - logging.FileHandler(log_file), - logging.StreamHandler() - ] - ) - self.logger = logging.getLogger(__name__) - - async def get_system_info(self): - """Get system information""" - try: - cpu_percent = psutil.cpu_percent(interval=1) - memory = psutil.virtual_memory() - disk = psutil.disk_usage('/') - - return { - 'status': 'online', - 'cpu_percent': cpu_percent, - 'memory_percent': memory.percent, - 'memory_total': memory.total, - 'memory_used': memory.used, - 'disk_percent': disk.percent, - 'disk_total': disk.total, - 'disk_used': disk.used, - 'timestamp': time.time() - } - except Exception as e: - self.logger.error(f"Error getting system info: {e}") - return {'status': 'error', 'error': str(e)} - - async def send_heartbeat(self): - """Send heartbeat to master server""" - try: - system_info = await self.get_system_info() - - master_url = f"http://{self.config['agent']['master_host']}:{self.config['agent']['master_port']}" - - async with self.session.post( - f"{master_url}/api/agent/heartbeat", - json=system_info, - timeout=10 - ) as response: - if response.status == 200: - self.logger.debug("Heartbeat sent successfully") - else: - self.logger.warning(f"Heartbeat failed with status: {response.status}") - - except Exception as e: - self.logger.error(f"Error sending heartbeat: {e}") - - async def heartbeat_loop(self): - """Main heartbeat loop""" - interval = self.config.get('agent', {}).get('heartbeat_interval', 30) - - while self.running: - await self.send_heartbeat() - await asyncio.sleep(interval) - - async def start(self): - """Start the agent""" - self.logger.info("Starting PyGuardian Agent...") - self.running = True - - # Create HTTP session - self.session = aiohttp.ClientSession() - - try: - # Start heartbeat loop - await self.heartbeat_loop() - except Exception as e: - self.logger.error(f"Agent error: {e}") - finally: - await self.stop() - - async def stop(self): - """Stop the agent""" - self.logger.info("Stopping PyGuardian Agent...") - self.running = False - - if self.session: - await self.session.close() - -async def main(): - agent = PyGuardianAgent() - - # Handle signals - def signal_handler(signum, frame): - print(f"\nReceived signal {signum}, shutting down...") - asyncio.create_task(agent.stop()) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - try: - await agent.start() - except KeyboardInterrupt: - await agent.stop() - -if __name__ == "__main__": - asyncio.run(main()) -EOF - -chmod +x "$INSTALL_DIR/agent.py" - -# Install Python dependencies -log_info "Installing Python dependencies..." -python3 -m pip install --upgrade pip -python3 -m pip install aiohttp pyyaml psutil - -# Create systemd service -log_info "Creating systemd service..." -cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF -[Unit] -Description=PyGuardian Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -Group=root -WorkingDirectory=${INSTALL_DIR} -ExecStart=/usr/bin/python3 ${INSTALL_DIR}/agent.py -ExecReload=/bin/kill -HUP \$MAINPID -Restart=always -RestartSec=10 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - -# Reload systemd and enable service -log_info "Enabling systemd service..." -systemctl daemon-reload -systemctl enable "$SERVICE_NAME" - -# Start the service -log_info "Starting PyGuardian Agent..." -systemctl start "$SERVICE_NAME" - -# Check service status -sleep 3 -if systemctl is-active --quiet "$SERVICE_NAME"; then - log_success "PyGuardian Agent installed and started successfully!" - log_info "Service status: $(systemctl is-active $SERVICE_NAME)" - log_info "Check logs: journalctl -u $SERVICE_NAME -f" - log_info "Agent config: /etc/pyguardian-agent/config.yaml" - log_info "Agent logs: /var/log/pyguardian-agent/agent.log" -else - log_error "PyGuardian Agent failed to start" - log_info "Check service status: systemctl status $SERVICE_NAME" - log_info "Check logs: journalctl -u $SERVICE_NAME" - exit 1 -fi - -log_success "Installation completed!" -log_info "The agent should now be visible in your PyGuardian master server." -log_info "Use '/agents' command in Telegram to verify the agent connection." -EOF \ No newline at end of file diff --git a/.history/install_agent_20251125203709.sh b/.history/install_agent_20251125203709.sh deleted file mode 100644 index edc057f..0000000 --- a/.history/install_agent_20251125203709.sh +++ /dev/null @@ -1,370 +0,0 @@ -#!/bin/bash - -# PyGuardian Agent Installation Script -# Usage: ./install_agent.sh --master --port - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color - -# Default values -MASTER_IP="" -MASTER_PORT="8080" -AGENT_PORT="8081" -INSTALL_DIR="/opt/pyguardian-agent" -SERVICE_NAME="pyguardian-agent" - -# Functions -log_info() { - echo -e "${BLUE}[INFO]${NC} $1" -} - -log_success() { - echo -e "${GREEN}[SUCCESS]${NC} $1" -} - -log_warning() { - echo -e "${YELLOW}[WARNING]${NC} $1" -} - -log_error() { - echo -e "${RED}[ERROR]${NC} $1" -} - -show_usage() { - echo "PyGuardian Agent Installation Script" - echo "" - echo "Usage: $0 --master [OPTIONS]" - echo "" - echo "Required:" - echo " --master Master server IP address" - echo "" - echo "Optional:" - echo " --port Master server port (default: 8080)" - echo " --agent-port

Agent listen port (default: 8081)" - echo " --help Show this help" - echo "" - echo "Example:" - echo " $0 --master 192.168.1.100 --port 8080" -} - -# Parse command line arguments -while [[ $# -gt 0 ]]; do - case $1 in - --master) - MASTER_IP="$2" - shift 2 - ;; - --port) - MASTER_PORT="$2" - shift 2 - ;; - --agent-port) - AGENT_PORT="$2" - shift 2 - ;; - --help) - show_usage - exit 0 - ;; - *) - log_error "Unknown option: $1" - show_usage - exit 1 - ;; - esac -done - -# Validate required parameters -if [[ -z "$MASTER_IP" ]]; then - log_error "Master IP is required" - show_usage - exit 1 -fi - -# Check if running as root -if [[ $EUID -ne 0 ]]; then - log_error "This script must be run as root" - exit 1 -fi - -log_info "Starting PyGuardian Agent installation..." -log_info "Master Server: ${MASTER_IP}:${MASTER_PORT}" -log_info "Agent Port: ${AGENT_PORT}" - -# Check system requirements -log_info "Checking system requirements..." - -# Check Python version -if ! command -v python3 &> /dev/null; then - log_error "Python3 is required but not installed" - exit 1 -fi - -PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') -REQUIRED_VERSION="3.10" - -if ! python3 -c "import sys; sys.exit(0 if sys.version_info >= (3, 10) else 1)" 2>/dev/null; then - log_error "Python 3.10+ is required. Found: $PYTHON_VERSION" - exit 1 -fi - -log_success "Python version check passed: $PYTHON_VERSION" - -# Install system dependencies -log_info "Installing system dependencies..." - -if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv curl wget -elif command -v yum &> /dev/null; then - yum install -y python3-pip curl wget -elif command -v dnf &> /dev/null; then - dnf install -y python3-pip curl wget -else - log_warning "Unknown package manager. Please install python3-pip manually." -fi - -# Create installation directory -log_info "Creating installation directory..." -mkdir -p "$INSTALL_DIR" -mkdir -p /etc/pyguardian-agent -mkdir -p /var/log/pyguardian-agent - -# Create agent configuration -log_info "Creating agent configuration..." -cat > /etc/pyguardian-agent/config.yaml << EOF -# PyGuardian Agent Configuration - -agent: - port: ${AGENT_PORT} - master_host: "${MASTER_IP}" - master_port: ${MASTER_PORT} - heartbeat_interval: 30 - max_reconnect_attempts: 10 - reconnect_delay: 5 - -logging: - level: INFO - file: /var/log/pyguardian-agent/agent.log - max_size: 10MB - backup_count: 5 - -security: - ssl_verify: true - api_key_file: /etc/pyguardian-agent/api.key -EOF - -# Create simple agent script -log_info "Creating agent script..." -cat > "$INSTALL_DIR/agent.py" << 'EOF' -#!/usr/bin/env python3 -""" -PyGuardian Agent -Lightweight agent for cluster management -""" - -import asyncio -import json -import logging -import signal -import sys -import time -import yaml -import aiohttp -import psutil -from pathlib import Path - -class PyGuardianAgent: - def __init__(self, config_path="/etc/pyguardian-agent/config.yaml"): - self.config_path = config_path - self.config = self.load_config() - self.setup_logging() - self.session = None - self.running = False - - def load_config(self): - """Load agent configuration""" - try: - with open(self.config_path, 'r') as f: - return yaml.safe_load(f) - except Exception as e: - print(f"Error loading config: {e}") - sys.exit(1) - - def setup_logging(self): - """Setup logging configuration""" - log_file = self.config.get('logging', {}).get('file', '/var/log/pyguardian-agent/agent.log') - log_level = getattr(logging, self.config.get('logging', {}).get('level', 'INFO')) - - # Create log directory if it doesn't exist - Path(log_file).parent.mkdir(parents=True, exist_ok=True) - - logging.basicConfig( - level=log_level, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[ - logging.FileHandler(log_file), - logging.StreamHandler() - ] - ) - self.logger = logging.getLogger(__name__) - - async def get_system_info(self): - """Get system information""" - try: - cpu_percent = psutil.cpu_percent(interval=1) - memory = psutil.virtual_memory() - disk = psutil.disk_usage('/') - - return { - 'status': 'online', - 'cpu_percent': cpu_percent, - 'memory_percent': memory.percent, - 'memory_total': memory.total, - 'memory_used': memory.used, - 'disk_percent': disk.percent, - 'disk_total': disk.total, - 'disk_used': disk.used, - 'timestamp': time.time() - } - except Exception as e: - self.logger.error(f"Error getting system info: {e}") - return {'status': 'error', 'error': str(e)} - - async def send_heartbeat(self): - """Send heartbeat to master server""" - try: - system_info = await self.get_system_info() - - master_url = f"http://{self.config['agent']['master_host']}:{self.config['agent']['master_port']}" - - async with self.session.post( - f"{master_url}/api/agent/heartbeat", - json=system_info, - timeout=10 - ) as response: - if response.status == 200: - self.logger.debug("Heartbeat sent successfully") - else: - self.logger.warning(f"Heartbeat failed with status: {response.status}") - - except Exception as e: - self.logger.error(f"Error sending heartbeat: {e}") - - async def heartbeat_loop(self): - """Main heartbeat loop""" - interval = self.config.get('agent', {}).get('heartbeat_interval', 30) - - while self.running: - await self.send_heartbeat() - await asyncio.sleep(interval) - - async def start(self): - """Start the agent""" - self.logger.info("Starting PyGuardian Agent...") - self.running = True - - # Create HTTP session - self.session = aiohttp.ClientSession() - - try: - # Start heartbeat loop - await self.heartbeat_loop() - except Exception as e: - self.logger.error(f"Agent error: {e}") - finally: - await self.stop() - - async def stop(self): - """Stop the agent""" - self.logger.info("Stopping PyGuardian Agent...") - self.running = False - - if self.session: - await self.session.close() - -async def main(): - agent = PyGuardianAgent() - - # Handle signals - def signal_handler(signum, frame): - print(f"\nReceived signal {signum}, shutting down...") - asyncio.create_task(agent.stop()) - - signal.signal(signal.SIGINT, signal_handler) - signal.signal(signal.SIGTERM, signal_handler) - - try: - await agent.start() - except KeyboardInterrupt: - await agent.stop() - -if __name__ == "__main__": - asyncio.run(main()) -EOF - -chmod +x "$INSTALL_DIR/agent.py" - -# Install Python dependencies -log_info "Installing Python dependencies..." -python3 -m pip install --upgrade pip -python3 -m pip install aiohttp pyyaml psutil - -# Create systemd service -log_info "Creating systemd service..." -cat > "/etc/systemd/system/${SERVICE_NAME}.service" << EOF -[Unit] -Description=PyGuardian Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -Group=root -WorkingDirectory=${INSTALL_DIR} -ExecStart=/usr/bin/python3 ${INSTALL_DIR}/agent.py -ExecReload=/bin/kill -HUP \$MAINPID -Restart=always -RestartSec=10 -StandardOutput=journal -StandardError=journal - -[Install] -WantedBy=multi-user.target -EOF - -# Reload systemd and enable service -log_info "Enabling systemd service..." -systemctl daemon-reload -systemctl enable "$SERVICE_NAME" - -# Start the service -log_info "Starting PyGuardian Agent..." -systemctl start "$SERVICE_NAME" - -# Check service status -sleep 3 -if systemctl is-active --quiet "$SERVICE_NAME"; then - log_success "PyGuardian Agent installed and started successfully!" - log_info "Service status: $(systemctl is-active $SERVICE_NAME)" - log_info "Check logs: journalctl -u $SERVICE_NAME -f" - log_info "Agent config: /etc/pyguardian-agent/config.yaml" - log_info "Agent logs: /var/log/pyguardian-agent/agent.log" -else - log_error "PyGuardian Agent failed to start" - log_info "Check service status: systemctl status $SERVICE_NAME" - log_info "Check logs: journalctl -u $SERVICE_NAME" - exit 1 -fi - -log_success "Installation completed!" -log_info "The agent should now be visible in your PyGuardian master server." -log_info "Use '/agents' command in Telegram to verify the agent connection." -EOF \ No newline at end of file diff --git a/.history/main_20251125195014.py b/.history/main_20251125195014.py deleted file mode 100644 index a6da712..0000000 --- a/.history/main_20251125195014.py +++ /dev/null @@ -1,389 +0,0 @@ -#!/usr/bin/env python3 -""" -PyGuardian - Linux Server Protection System -Главный файл для запуска системы мониторинга и защиты - -Автор: PyGuardian 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 - - -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.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.attack_detector = AttackDetector( - self.storage, - self.firewall_manager, - { - **self.config['security'], - 'whitelist': self.config.get('whitelist', []) - } - ) - self.logger.info("Attack Detector инициализирован") - - # 4. Инициализация Telegram бота - self.telegram_bot = TelegramBot( - self.config['telegram'], - self.storage, - self.firewall_manager, - self.attack_detector - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125200046.py b/.history/main_20251125200046.py deleted file mode 100644 index 27cb7d3..0000000 --- a/.history/main_20251125200046.py +++ /dev/null @@ -1,389 +0,0 @@ -#!/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 - - -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.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.attack_detector = AttackDetector( - self.storage, - self.firewall_manager, - { - **self.config['security'], - 'whitelist': self.config.get('whitelist', []) - } - ) - self.logger.info("Attack Detector инициализирован") - - # 4. Инициализация Telegram бота - self.telegram_bot = TelegramBot( - self.config['telegram'], - self.storage, - self.firewall_manager, - self.attack_detector - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125200752.py b/.history/main_20251125200752.py deleted file mode 100644 index 8846b49..0000000 --- a/.history/main_20251125200752.py +++ /dev/null @@ -1,392 +0,0 @@ -#!/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.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.attack_detector = AttackDetector( - self.storage, - self.firewall_manager, - { - **self.config['security'], - 'whitelist': self.config.get('whitelist', []) - } - ) - self.logger.info("Attack Detector инициализирован") - - # 4. Инициализация Telegram бота - self.telegram_bot = TelegramBot( - self.config['telegram'], - self.storage, - self.firewall_manager, - self.attack_detector - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125200804.py b/.history/main_20251125200804.py deleted file mode 100644 index 3e4b8ba..0000000 --- a/.history/main_20251125200804.py +++ /dev/null @@ -1,397 +0,0 @@ -#!/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.attack_detector = AttackDetector( - self.storage, - self.firewall_manager, - { - **self.config['security'], - 'whitelist': self.config.get('whitelist', []) - } - ) - self.logger.info("Attack Detector инициализирован") - - # 4. Инициализация Telegram бота - self.telegram_bot = TelegramBot( - self.config['telegram'], - self.storage, - self.firewall_manager, - self.attack_detector - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125200819.py b/.history/main_20251125200819.py deleted file mode 100644 index b930005..0000000 --- a/.history/main_20251125200819.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/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. Инициализация новых менеджеров безопасности - encryption_key = self.config.get('security', {}).get('encryption_key') - self.password_manager = PasswordManager(encryption_key) - self.session_manager = SessionManager() - self.security_manager = SecurityManager( - self.storage, - self.firewall_manager, - self.session_manager, - self.password_manager, - self.config.get('security', {}) - ) - self.logger.info("Менеджеры безопасности инициализированы") - - # 4. Инициализация детектора атак с security manager - self.attack_detector = AttackDetector( - self.storage, - self.firewall_manager, - { - **self.config['security'], - 'whitelist': self.config.get('whitelist', []) - }, - self.security_manager - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125200837.py b/.history/main_20251125200837.py deleted file mode 100644 index b8e5890..0000000 --- a/.history/main_20251125200837.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/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 - self.attack_detector = AttackDetector( - self.storage, - self.firewall_manager, - { - **self.config['security'], - 'whitelist': self.config.get('whitelist', []) - }, - self.security_manager - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125200856.py b/.history/main_20251125200856.py deleted file mode 100644 index 10ebe86..0000000 --- a/.history/main_20251125200856.py +++ /dev/null @@ -1,412 +0,0 @@ -#!/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) \ No newline at end of file diff --git a/.history/main_20251125200913.py b/.history/main_20251125200913.py deleted file mode 100644 index 0fa6831..0000000 --- a/.history/main_20251125200913.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/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. Инициализация новых менеджеров безопасности - password_config = self.config.get('passwords', {}) - self.password_manager = PasswordManager(password_config) - self.session_manager = SessionManager() - security_config = self.config.get('security', {}) - self.security_manager = SecurityManager( - self.storage, - self.firewall_manager, - security_config - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125202055.py b/.history/main_20251125202055.py deleted file mode 100644 index 0fa6831..0000000 --- a/.history/main_20251125202055.py +++ /dev/null @@ -1,414 +0,0 @@ -#!/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. Инициализация новых менеджеров безопасности - password_config = self.config.get('passwords', {}) - self.password_manager = PasswordManager(password_config) - self.session_manager = SessionManager() - security_config = self.config.get('security', {}) - self.security_manager = SecurityManager( - self.storage, - self.firewall_manager, - security_config - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125202502.py b/.history/main_20251125202502.py deleted file mode 100644 index 2ebe4b4..0000000 --- a/.history/main_20251125202502.py +++ /dev/null @@ -1,416 +0,0 @@ -#!/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 -from src.cluster_manager import ClusterManager - - -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.cluster_manager: Optional[ClusterManager] = 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. Инициализация новых менеджеров безопасности - password_config = self.config.get('passwords', {}) - self.password_manager = PasswordManager(password_config) - self.session_manager = SessionManager() - security_config = self.config.get('security', {}) - self.security_manager = SecurityManager( - self.storage, - self.firewall_manager, - security_config - ) - 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) \ No newline at end of file diff --git a/.history/main_20251125202514.py b/.history/main_20251125202514.py deleted file mode 100644 index 52fb4b3..0000000 --- a/.history/main_20251125202514.py +++ /dev/null @@ -1,426 +0,0 @@ -#!/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 -from src.cluster_manager import ClusterManager - - -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.cluster_manager: Optional[ClusterManager] = 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. Инициализация новых менеджеров безопасности - password_config = self.config.get('passwords', {}) - self.password_manager = PasswordManager(password_config) - self.session_manager = SessionManager() - security_config = self.config.get('security', {}) - self.security_manager = SecurityManager( - self.storage, - self.firewall_manager, - security_config - ) - 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.cluster_manager - ) - self.logger.info("Telegram Bot инициализирован") - - # 6. Инициализация менеджера кластера - cluster_config = self.config.get('cluster', {}) - self.cluster_manager = ClusterManager(self.storage, cluster_config) - await self.cluster_manager.load_agents() - self.logger.info("Cluster Manager инициализирован") - - # Обновляем ссылку в боте - self.telegram_bot.cluster_manager = self.cluster_manager - - # 7. Инициализация менеджера уведомлений - 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) \ No newline at end of file diff --git a/.history/main_20251125203709.py b/.history/main_20251125203709.py deleted file mode 100644 index 52fb4b3..0000000 --- a/.history/main_20251125203709.py +++ /dev/null @@ -1,426 +0,0 @@ -#!/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 -from src.cluster_manager import ClusterManager - - -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.cluster_manager: Optional[ClusterManager] = 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. Инициализация новых менеджеров безопасности - password_config = self.config.get('passwords', {}) - self.password_manager = PasswordManager(password_config) - self.session_manager = SessionManager() - security_config = self.config.get('security', {}) - self.security_manager = SecurityManager( - self.storage, - self.firewall_manager, - security_config - ) - 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.cluster_manager - ) - self.logger.info("Telegram Bot инициализирован") - - # 6. Инициализация менеджера кластера - cluster_config = self.config.get('cluster', {}) - self.cluster_manager = ClusterManager(self.storage, cluster_config) - await self.cluster_manager.load_agents() - self.logger.info("Cluster Manager инициализирован") - - # Обновляем ссылку в боте - self.telegram_bot.cluster_manager = self.cluster_manager - - # 7. Инициализация менеджера уведомлений - 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) \ No newline at end of file diff --git a/.history/mkdocs_20251125211158.yml b/.history/mkdocs_20251125211158.yml deleted file mode 100644 index 894ddc4..0000000 --- a/.history/mkdocs_20251125211158.yml +++ /dev/null @@ -1,74 +0,0 @@ -site_name: PyGuardian Documentation -site_description: AI-Powered Security & Cluster Management System -site_author: SmartSolTech -site_url: https://smartsoltech.github.io/PyGuardian - -repo_name: SmartSolTech/PyGuardian -repo_url: https://github.com/SmartSolTech/PyGuardian - -theme: - name: material - palette: - - scheme: default - primary: blue - accent: blue - toggle: - icon: material/brightness-7 - name: Switch to dark mode - - scheme: slate - primary: blue - accent: blue - toggle: - icon: material/brightness-4 - name: Switch to light mode - features: - - navigation.tabs - - navigation.sections - - navigation.expand - - navigation.top - - search.highlight - - search.share - - content.code.annotate - -plugins: - - search - - git-revision-date-localized: - enable_creation_date: true - -markdown_extensions: - - admonition - - pymdownx.details - - pymdownx.superfences - - pymdownx.highlight: - anchor_linenums: true - - pymdownx.inlinehilite - - pymdownx.snippets - - pymdownx.tabbed: - alternate_style: true - - pymdownx.tasklist: - custom_checkbox: true - - attr_list - - md_in_html - - toc: - permalink: true - -nav: - - Home: 'README.md' - - Quick Start: 'documentation/guides/QUICKSTART.md' - - Installation: 'documentation/examples/INSTALLATION.md' - - Architecture: 'documentation/guides/ARCHITECTURE.md' - - Cluster Setup: 'documentation/guides/CLUSTER_SETUP.md' - - Configuration: - - 'Example Configs': 'documentation/examples/configurations.md' - - 'Telegram Commands': 'documentation/examples/telegram-commands.md' - - 'Cluster Management': 'documentation/examples/cluster-management.md' - - Development: 'DEVELOPMENT_SUMMARY.md' - -extra: - social: - - icon: fontawesome/brands/github - link: https://github.com/SmartSolTech/PyGuardian - - icon: fontawesome/brands/telegram - link: https://t.me/PyGuardianSupport - -copyright: Copyright © 2024 SmartSolTech \ No newline at end of file diff --git a/.history/pytest_20251125211452.ini b/.history/pytest_20251125211452.ini deleted file mode 100644 index 27916dd..0000000 --- a/.history/pytest_20251125211452.ini +++ /dev/null @@ -1,25 +0,0 @@ -[tool:pytest] -testpaths = tests -python_files = test_*.py -python_classes = Test* -python_functions = test_* -addopts = - -v - --tb=short - --strict-markers - --disable-warnings - --color=yes - -markers = - unit: Unit tests - integration: Integration tests - e2e: End-to-end tests - slow: Slow tests - auth: Authentication tests - api: API tests - cluster: Cluster management tests - security: Security tests - -filterwarnings = - ignore::DeprecationWarning - ignore::PendingDeprecationWarning \ No newline at end of file diff --git a/.history/requirements_20251125195057.txt b/.history/requirements_20251125195057.txt deleted file mode 100644 index 3548d2c..0000000 --- a/.history/requirements_20251125195057.txt +++ /dev/null @@ -1,45 +0,0 @@ -# PyGuardian Requirements -# ======================== - -# Асинхронная работа с SQLite -aiosqlite>=0.19.0 - -# Асинхронная работа с файлами -aiofiles>=23.2.0 - -# Telegram Bot API -python-telegram-bot>=20.7 - -# YAML конфигурация -PyYAML>=6.0.1 - -# Работа с IP адресами (встроенный в Python 3.3+) -# ipaddress - встроенный модуль - -# Для работы с регулярными выражениями (встроенный) -# re - встроенный модуль - -# Для работы с датами (встроенный) -# datetime - встроенный модуль - -# Для работы с системными вызовами (встроенный) -# subprocess - встроенный модуль - -# Для асинхронности (встроенный в Python 3.7+) -# asyncio - встроенный модуль - -# Для логирования (встроенный) -# logging - встроенный модуль - -# Для работы с путями (встроенный в Python 3.4+) -# pathlib - встроенный модуль - -# Для сигналов (встроенный) -# signal - встроенный модуль - -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file diff --git a/.history/requirements_20251125202055.txt b/.history/requirements_20251125202055.txt deleted file mode 100644 index 3548d2c..0000000 --- a/.history/requirements_20251125202055.txt +++ /dev/null @@ -1,45 +0,0 @@ -# PyGuardian Requirements -# ======================== - -# Асинхронная работа с SQLite -aiosqlite>=0.19.0 - -# Асинхронная работа с файлами -aiofiles>=23.2.0 - -# Telegram Bot API -python-telegram-bot>=20.7 - -# YAML конфигурация -PyYAML>=6.0.1 - -# Работа с IP адресами (встроенный в Python 3.3+) -# ipaddress - встроенный модуль - -# Для работы с регулярными выражениями (встроенный) -# re - встроенный модуль - -# Для работы с датами (встроенный) -# datetime - встроенный модуль - -# Для работы с системными вызовами (встроенный) -# subprocess - встроенный модуль - -# Для асинхронности (встроенный в Python 3.7+) -# asyncio - встроенный модуль - -# Для логирования (встроенный) -# logging - встроенный модуль - -# Для работы с путями (встроенный в Python 3.4+) -# pathlib - встроенный модуль - -# Для сигналов (встроенный) -# signal - встроенный модуль - -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file diff --git a/.history/requirements_20251125202544.txt b/.history/requirements_20251125202544.txt deleted file mode 100644 index 161e3f7..0000000 --- a/.history/requirements_20251125202544.txt +++ /dev/null @@ -1,54 +0,0 @@ -# PyGuardian Requirements -# ======================== - -# Асинхронная работа с SQLite -aiosqlite>=0.19.0 - -# Асинхронная работа с файлами -aiofiles>=23.2.0 - -# Telegram Bot API -python-telegram-bot>=20.7 - -# YAML конфигурация -PyYAML>=6.0.1 - -# SSH соединения для управления кластером -paramiko>=3.3.1 - -# Шифрование для паролей и данных кластера -cryptography>=41.0.0 - -# Системная информация и управление процессами -psutil>=5.9.0 - -# Работа с IP адресами (встроенный в Python 3.3+) -# ipaddress - встроенный модуль - -# Для работы с регулярными выражениями (встроенный) -# re - встроенный модуль - -# Для работы с датами (встроенный) -# datetime - встроенный модуль - -# Для работы с системными вызовами (встроенный) -# subprocess - встроенный модуль - -# Для асинхронности (встроенный в Python 3.7+) -# asyncio - встроенный модуль - -# Для логирования (встроенный) -# logging - встроенный модуль - -# Для работы с путями (встроенный в Python 3.4+) -# pathlib - встроенный модуль - -# Для сигналов (встроенный) -# signal - встроенный модуль - -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file diff --git a/.history/requirements_20251125203709.txt b/.history/requirements_20251125203709.txt deleted file mode 100644 index 161e3f7..0000000 --- a/.history/requirements_20251125203709.txt +++ /dev/null @@ -1,54 +0,0 @@ -# PyGuardian Requirements -# ======================== - -# Асинхронная работа с SQLite -aiosqlite>=0.19.0 - -# Асинхронная работа с файлами -aiofiles>=23.2.0 - -# Telegram Bot API -python-telegram-bot>=20.7 - -# YAML конфигурация -PyYAML>=6.0.1 - -# SSH соединения для управления кластером -paramiko>=3.3.1 - -# Шифрование для паролей и данных кластера -cryptography>=41.0.0 - -# Системная информация и управление процессами -psutil>=5.9.0 - -# Работа с IP адресами (встроенный в Python 3.3+) -# ipaddress - встроенный модуль - -# Для работы с регулярными выражениями (встроенный) -# re - встроенный модуль - -# Для работы с датами (встроенный) -# datetime - встроенный модуль - -# Для работы с системными вызовами (встроенный) -# subprocess - встроенный модуль - -# Для асинхронности (встроенный в Python 3.7+) -# asyncio - встроенный модуль - -# Для логирования (встроенный) -# logging - встроенный модуль - -# Для работы с путями (встроенный в Python 3.4+) -# pathlib - встроенный модуль - -# Для сигналов (встроенный) -# signal - встроенный модуль - -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file diff --git a/.history/requirements_20251125205426.txt b/.history/requirements_20251125205426.txt deleted file mode 100644 index 2c5a762..0000000 --- a/.history/requirements_20251125205426.txt +++ /dev/null @@ -1,57 +0,0 @@ -# PyGuardian Requirements -# ======================== - -# Асинхронная работа с SQLite -aiosqlite>=0.19.0 - -# Асинхронная работа с файлами -aiofiles>=23.2.0 - -# Telegram Bot API -python-telegram-bot>=20.7 - -# YAML конфигурация -PyYAML>=6.0.1 - -# SSH соединения для управления кластером -paramiko>=3.3.1 - -# Шифрование для паролей и данных кластера -cryptography>=41.0.0 - -# JWT токены для аутентификации агентов -PyJWT>=2.8.0 - -# Системная информация и управление процессами -psutil>=5.9.0 - -# Работа с IP адресами (встроенный в Python 3.3+) -# ipaddress - встроенный модуль - -# Для работы с регулярными выражениями (встроенный) -# re - встроенный модуль - -# Для работы с датами (встроенный) -# datetime - встроенный модуль - -# Для работы с системными вызовами (встроенный) -# subprocess - встроенный модуль - -# Для асинхронности (встроенный в Python 3.7+) -# asyncio - встроенный модуль - -# Для логирования (встроенный) -# logging - встроенный модуль - -# Для работы с путями (встроенный в Python 3.4+) -# pathlib - встроенный модуль - -# Для сигналов (встроенный) -# signal - встроенный модуль - -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file diff --git a/.history/requirements_20251125210433.txt b/.history/requirements_20251125212536.txt similarity index 89% rename from .history/requirements_20251125210433.txt rename to .history/requirements_20251125212536.txt index e3e6255..d213ebf 100644 --- a/.history/requirements_20251125210433.txt +++ b/.history/requirements_20251125212536.txt @@ -53,9 +53,14 @@ psutil>=5.9.0 # Для сигналов (встроенный) # signal - встроенный модуль -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file +# Дополнительные зависимости для разработки и тестирования +pytest>=7.4.0 +pytest-asyncio>=0.21.0 +pytest-cov>=4.1.0 +black>=23.9.0 +flake8>=6.0.0 +isort>=5.12.0 +bandit>=1.7.5 +safety>=2.3.0 +mkdocs>=1.5.0 +mkdocs-material>=9.4.0 \ No newline at end of file diff --git a/.history/requirements_20251125205915.txt b/.history/requirements_20251125212551.txt similarity index 89% rename from .history/requirements_20251125205915.txt rename to .history/requirements_20251125212551.txt index e3e6255..d213ebf 100644 --- a/.history/requirements_20251125205915.txt +++ b/.history/requirements_20251125212551.txt @@ -53,9 +53,14 @@ psutil>=5.9.0 # Для сигналов (встроенный) # signal - встроенный модуль -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file +# Дополнительные зависимости для разработки и тестирования +pytest>=7.4.0 +pytest-asyncio>=0.21.0 +pytest-cov>=4.1.0 +black>=23.9.0 +flake8>=6.0.0 +isort>=5.12.0 +bandit>=1.7.5 +safety>=2.3.0 +mkdocs>=1.5.0 +mkdocs-material>=9.4.0 \ No newline at end of file diff --git a/.history/scripts/deployment-report_20251125204904.sh b/.history/scripts/deployment-report_20251125204904.sh deleted file mode 100644 index 5f3b1b9..0000000 --- a/.history/scripts/deployment-report_20251125204904.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/bash - -#========================================================================== -# PyGuardian Final Deployment Report -# Финальный отчет о завершенной реализации -#========================================================================== - -echo "🎉 PyGuardian System - Deployment Complete! 🎉" -echo "================================================" -echo "" - -# Статистика проекта -echo "📊 PROJECT STATISTICS:" -echo "• Total lines of code and docs: 10,169+" -echo "• Python source files: 8 modules (4,985 lines)" -echo "• Installation scripts: 3 scripts (1,780 lines)" -echo "• Documentation files: 8 documents (2,404 lines)" -echo "" - -# Ключевые компоненты -echo "🔧 KEY COMPONENTS IMPLEMENTED:" -echo "• ✅ ClusterManager - Centralized cluster management (621 lines)" -echo "• ✅ Telegram Bot - Advanced interactive commands (1,344 lines)" -echo "• ✅ Universal Installer - Multi-mode deployment (735 lines)" -echo "• ✅ Docker Support - Containerized deployment (690 lines)" -echo "• ✅ Security System - Advanced threat detection (515 lines)" -echo "• ✅ Storage Management - Database operations (607 lines)" -echo "• ✅ Comprehensive Documentation - Complete user guides" -echo "" - -# Возможности -echo "🚀 CAPABILITIES DELIVERED:" -echo "• 🌐 Centralized cluster management via Telegram bot" -echo "• 🚀 Automatic agent deployment over SSH" -echo "• 🔧 Three deployment modes: standalone/controller/agent" -echo "• 🐳 Full Docker containerization support" -echo "• 📱 Interactive Telegram interface with 50+ commands" -echo "• 🛡️ Advanced security monitoring and protection" -echo "• 📊 Real-time monitoring and alerting" -echo "• 🔒 Enterprise-grade security features" -echo "" - -# Архитектура -echo "🏗️ SYSTEM ARCHITECTURE:" -echo "• Asyncio-based high-performance Python backend" -echo "• RESTful API for controller-agent communication" -echo "• SQLite/PostgreSQL database support" -echo "• systemd service integration" -echo "• Docker containerization with privilege management" -echo "• Event-driven notification system" -echo "" - -# Развертывание -echo "📦 DEPLOYMENT OPTIONS:" -echo "• Standalone: ./install.sh" -echo "• Controller: ./install.sh --mode controller" -echo "• Agent: ./install.sh --mode agent --controller " -echo "• Docker: ./scripts/docker-install.sh" -echo "• Makefile: make install|controller|agent" -echo "" - -# Тестирование -echo "🧪 TESTING & VALIDATION:" -echo "• Installation test suite: ./scripts/test-install.sh" -echo "• Syntax validation for all scripts" -echo "• Configuration validation" -echo "• Dependency checking" -echo "• Service health monitoring" -echo "" - -echo "================================================" -echo "🎯 MISSION ACCOMPLISHED!" -echo "" -echo "The user requested:" -echo "'🟣 10. Возможность централизованного развертывания агентов'" -echo "" -echo "✅ DELIVERED:" -echo "• Complete cluster management system" -echo "• Centralized Telegram bot control" -echo "• Automatic agent deployment capabilities" -echo "• Universal installation system" -echo "• Comprehensive documentation" -echo "" -echo "🛡️ PyGuardian is now a production-ready" -echo " enterprise security management platform!" -echo "" -echo "⚡ Quick Start:" -echo " sudo ./install.sh" -echo " # Configure Telegram bot" -echo " # Start securing your infrastructure!" -echo "" -echo "📖 Documentation:" -echo " • QUICKSTART.md - Fast deployment guide" -echo " • docs/INSTALLATION.md - Detailed setup" -echo " • docs/CLUSTER_SETUP.md - Cluster configuration" -echo "" -echo "🆘 Support:" -echo " • ./scripts/test-install.sh - System testing" -echo " • /debug export - Telegram bot diagnostics" -echo " • GitHub Issues for community support" -echo "" -echo "================================================" -echo "🎉 Ready to secure the world! 🌍🛡️" -echo "================================================" \ No newline at end of file diff --git a/.history/scripts/docker-install_20251125203603.sh b/.history/scripts/docker-install_20251125203603.sh deleted file mode 100644 index 19bd485..0000000 --- a/.history/scripts/docker-install_20251125203603.sh +++ /dev/null @@ -1,691 +0,0 @@ -#!/bin/bash - -#========================================================================== -# PyGuardian Docker Installation Script -# Supports containerized deployment for Controller and Agent modes -# Author: SmartSolTech Team -# Version: 2.0 -#========================================================================== - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[1;33m' -NC='\033[0m' - -# Global variables -INSTALL_MODE="" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -DOCKER_COMPOSE_VERSION="2.20.0" - -# Configuration variables -TELEGRAM_BOT_TOKEN="" -ADMIN_ID="" -CONTROLLER_URL="" -AGENT_TOKEN="" -CONTROLLER_PORT="8080" - -#========================================================================== -# Helper functions -#========================================================================== - -print_header() { - echo -e "${BLUE}" - echo "==============================================" - echo " PyGuardian Docker $1 Installation" - echo "==============================================" - echo -e "${NC}" -} - -print_success() { - echo -e "${GREEN}✓ $1${NC}" -} - -print_error() { - echo -e "${RED}✗ $1${NC}" -} - -print_info() { - echo -e "${BLUE}ℹ $1${NC}" -} - -# Check if running as root -check_root() { - if [[ $EUID -ne 0 ]]; then - print_error "This script must be run as root or with sudo" - exit 1 - fi -} - -# Parse command line arguments -parse_args() { - while [[ $# -gt 0 ]]; do - case $1 in - --mode=*) - INSTALL_MODE="${1#*=}" - shift - ;; - --controller-url=*) - CONTROLLER_URL="${1#*=}" - shift - ;; - --agent-token=*) - AGENT_TOKEN="${1#*=}" - shift - ;; - --telegram-token=*) - TELEGRAM_BOT_TOKEN="${1#*=}" - shift - ;; - --admin-id=*) - ADMIN_ID="${1#*=}" - shift - ;; - --port=*) - CONTROLLER_PORT="${1#*=}" - shift - ;; - -h|--help) - show_usage - exit 0 - ;; - *) - print_error "Unknown option: $1" - show_usage - exit 1 - ;; - esac - done -} - -show_usage() { - echo "Usage: $0 [OPTIONS]" - echo "" - echo "OPTIONS:" - echo " --mode=MODE Installation mode: controller, agent" - echo " --controller-url=URL Controller URL (for agent mode)" - echo " --agent-token=TOKEN Agent authentication token" - echo " --telegram-token=TOKEN Telegram bot token" - echo " --admin-id=ID Telegram admin ID" - echo " --port=PORT Controller port (default: 8080)" - echo " -h, --help Show this help" -} - -# Select installation mode -select_install_mode() { - print_info "Выберите режим Docker установки:" - echo "" - echo "1) Controller - Центральный контроллер кластера в Docker" - echo "2) Agent - Агент в Docker для подключения к контроллеру" - echo "" - - while true; do - read -p "Выберите режим (1-2): " choice - case $choice in - 1) - INSTALL_MODE="controller" - break - ;; - 2) - INSTALL_MODE="agent" - break - ;; - *) - print_error "Неверный выбор. Введите 1 или 2." - ;; - esac - done -} - -# Check Docker requirements -check_docker_requirements() { - print_info "Проверка Docker требований..." - - # Check if Docker is installed - if ! command -v docker &> /dev/null; then - print_info "Docker не установлен. Устанавливаем Docker..." - install_docker - else - print_success "Docker уже установлен: $(docker --version)" - fi - - # Check if Docker Compose is installed - if ! command -v docker-compose &> /dev/null; then - print_info "Docker Compose не установлен. Устанавливаем..." - install_docker_compose - else - print_success "Docker Compose уже установлен: $(docker-compose --version)" - fi - - # Start Docker service - systemctl start docker - systemctl enable docker - print_success "Docker service started and enabled" -} - -# Install Docker -install_docker() { - print_info "Установка Docker..." - - # Install prerequisites - if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y ca-certificates curl gnupg - - # Add Docker's official GPG key - install -m 0755 -d /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - - # Add repository - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null - - apt-get update - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - - elif command -v yum &> /dev/null; then - yum install -y yum-utils - yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo - yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - - else - print_error "Unsupported package manager for Docker installation" - exit 1 - fi - - print_success "Docker installed successfully" -} - -# Install Docker Compose -install_docker_compose() { - print_info "Установка Docker Compose..." - - curl -L "https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose - - print_success "Docker Compose installed successfully" -} - -# Get configuration for controller -get_controller_config() { - if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then - echo "" - print_info "Настройка Telegram бота для контроллера:" - echo "1. Создайте бота у @BotFather" - echo "2. Получите токен бота" - echo "3. Узнайте ваш chat ID у @userinfobot" - echo "" - read -p "Введите токен Telegram бота: " TELEGRAM_BOT_TOKEN - fi - - if [[ -z "$ADMIN_ID" ]]; then - read -p "Введите ваш Telegram ID (admin): " ADMIN_ID - fi - - read -p "Порт для API контроллера (по умолчанию $CONTROLLER_PORT): " input_port - CONTROLLER_PORT=${input_port:-$CONTROLLER_PORT} -} - -# Get configuration for agent -get_agent_config() { - if [[ -z "$CONTROLLER_URL" ]]; then - read -p "URL контроллера (например, https://controller.example.com:8080): " CONTROLLER_URL - fi - - if [[ -z "$AGENT_TOKEN" ]]; then - read -p "Токен агента (получите у администратора контроллера): " AGENT_TOKEN - fi - - read -p "Имя агента (по умолчанию: $(hostname)): " AGENT_NAME - AGENT_NAME=${AGENT_NAME:-$(hostname)} -} - -# Create Dockerfile for controller -create_controller_dockerfile() { - print_info "Создание Dockerfile для контроллера..." - - mkdir -p controller - - cat > controller/Dockerfile < agent/Dockerfile < controller-config.yaml < agent-config.yaml < docker-compose.yml < docker-compose.yml < /dev/null; then - print_info "Docker не установлен. Устанавливаем Docker..." - install_docker - else - print_success "Docker уже установлен: $(docker --version)" - fi - - # Check if Docker Compose is installed - if ! command -v docker-compose &> /dev/null; then - print_info "Docker Compose не установлен. Устанавливаем..." - install_docker_compose - else - print_success "Docker Compose уже установлен: $(docker-compose --version)" - fi - - # Start Docker service - systemctl start docker - systemctl enable docker - print_success "Docker service started and enabled" -} - -# Install Docker -install_docker() { - print_info "Установка Docker..." - - # Install prerequisites - if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y ca-certificates curl gnupg - - # Add Docker's official GPG key - install -m 0755 -d /etc/apt/keyrings - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg - chmod a+r /etc/apt/keyrings/docker.gpg - - # Add repository - echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null - - apt-get update - apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - - elif command -v yum &> /dev/null; then - yum install -y yum-utils - yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo - yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin - - else - print_error "Unsupported package manager for Docker installation" - exit 1 - fi - - print_success "Docker installed successfully" -} - -# Install Docker Compose -install_docker_compose() { - print_info "Установка Docker Compose..." - - curl -L "https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - chmod +x /usr/local/bin/docker-compose - - print_success "Docker Compose installed successfully" -} - -# Get configuration for controller -get_controller_config() { - if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then - echo "" - print_info "Настройка Telegram бота для контроллера:" - echo "1. Создайте бота у @BotFather" - echo "2. Получите токен бота" - echo "3. Узнайте ваш chat ID у @userinfobot" - echo "" - read -p "Введите токен Telegram бота: " TELEGRAM_BOT_TOKEN - fi - - if [[ -z "$ADMIN_ID" ]]; then - read -p "Введите ваш Telegram ID (admin): " ADMIN_ID - fi - - read -p "Порт для API контроллера (по умолчанию $CONTROLLER_PORT): " input_port - CONTROLLER_PORT=${input_port:-$CONTROLLER_PORT} -} - -# Get configuration for agent -get_agent_config() { - if [[ -z "$CONTROLLER_URL" ]]; then - read -p "URL контроллера (например, https://controller.example.com:8080): " CONTROLLER_URL - fi - - if [[ -z "$AGENT_TOKEN" ]]; then - read -p "Токен агента (получите у администратора контроллера): " AGENT_TOKEN - fi - - read -p "Имя агента (по умолчанию: $(hostname)): " AGENT_NAME - AGENT_NAME=${AGENT_NAME:-$(hostname)} -} - -# Create Dockerfile for controller -create_controller_dockerfile() { - print_info "Создание Dockerfile для контроллера..." - - mkdir -p controller - - cat > controller/Dockerfile < agent/Dockerfile < controller-config.yaml < agent-config.yaml < docker-compose.yml < docker-compose.yml < /dev/null; then - print_error "Python 3 is required but not installed" - exit 1 - fi - - PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') - print_success "Python version: $PYTHON_VERSION" - - # Check if Python version is >= 3.10 - if ! python3 -c 'import sys; exit(0 if sys.version_info >= (3, 10) else 1)'; then - print_error "Python 3.10+ is required, but $PYTHON_VERSION is installed" - exit 1 - fi - - # Check pip - if ! command -v pip3 &> /dev/null; then - print_error "pip3 is required but not installed" - exit 1 - fi - print_success "pip3 is available" - - # Check firewall - if command -v iptables &> /dev/null; then - print_success "iptables is available" - elif command -v nft &> /dev/null; then - print_success "nftables is available" - else - print_warning "Neither iptables nor nftables found - firewall functionality may be limited" - fi -} - -# Install system dependencies -install_dependencies() { - print_info "Установка системных зависимостей..." - - # Detect package manager - if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv python3-dev build-essential \ - libssl-dev libffi-dev sqlite3 curl wget systemd - print_success "Dependencies installed (APT)" - elif command -v yum &> /dev/null; then - yum install -y python3-pip python3-devel gcc openssl-devel libffi-devel \ - sqlite curl wget systemd - print_success "Dependencies installed (YUM)" - elif command -v dnf &> /dev/null; then - dnf install -y python3-pip python3-devel gcc openssl-devel libffi-devel \ - sqlite curl wget systemd - print_success "Dependencies installed (DNF)" - else - print_error "Unsupported package manager" - exit 1 - fi -} - -# Create system user -create_user() { - print_info "Создание системного пользователя..." - - if ! id "$SERVICE_USER" &>/dev/null; then - useradd --system --create-home --shell /bin/bash "$SERVICE_USER" - print_success "User $SERVICE_USER created" - else - print_info "User $SERVICE_USER already exists" - fi -} - -# Create directories -create_directories() { - print_info "Создание директорий..." - - mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" - chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" "$LOG_DIR" "$DATA_DIR" - chmod 755 "$CONFIG_DIR" - chmod 750 "$LOG_DIR" "$DATA_DIR" - - print_success "Directories created" -} - -# Copy application files -copy_files() { - print_info "Копирование файлов приложения..." - - # Copy source code - cp -r "$PROJECT_DIR/src" "$INSTALL_DIR/" - cp "$PROJECT_DIR/main.py" "$INSTALL_DIR/" - cp "$PROJECT_DIR/requirements.txt" "$INSTALL_DIR/" - - # Copy configuration template - if [[ "$INSTALL_MODE" == "standalone" || "$INSTALL_MODE" == "controller" ]]; then - cp "$PROJECT_DIR/config/config.yaml" "$CONFIG_DIR/config.yaml.template" - fi - - # Set permissions - chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" - chmod +x "$INSTALL_DIR/main.py" - - print_success "Files copied" -} - -# Install Python dependencies -install_python_deps() { - print_info "Установка Python зависимостей..." - - # Create virtual environment - sudo -u "$SERVICE_USER" python3 -m venv "$INSTALL_DIR/venv" - - # Install dependencies - sudo -u "$SERVICE_USER" "$INSTALL_DIR/venv/bin/pip" install -r "$INSTALL_DIR/requirements.txt" - - print_success "Python dependencies installed" -} - -# Configure application based on mode -configure_application() { - print_info "Настройка приложения..." - - case "$INSTALL_MODE" in - "standalone") - configure_standalone - ;; - "controller") - configure_controller - ;; - "agent") - configure_agent - ;; - esac -} - -configure_standalone() { - print_info "Настройка автономного режима..." - - # Get configuration from user - if [[ "$NON_INTERACTIVE" != "true" ]]; then - get_telegram_config - fi - - # Create configuration file - create_standalone_config - print_success "Standalone configuration created" -} - -configure_controller() { - print_info "Настройка контроллера кластера..." - - # Get configuration from user - if [[ "$NON_INTERACTIVE" != "true" ]]; then - get_telegram_config - get_controller_config - fi - - # Create configuration file - create_controller_config - print_success "Controller configuration created" -} - -configure_agent() { - print_info "Настройка агента..." - - # Get configuration from user - if [[ "$NON_INTERACTIVE" != "true" ]]; then - get_agent_config - fi - - # Create configuration file - create_agent_config - print_success "Agent configuration created" -} - -get_telegram_config() { - if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then - echo "" - print_info "Настройка Telegram бота:" - echo "1. Создайте бота у @BotFather" - echo "2. Получите токен бота" - echo "3. Узнайте ваш chat ID у @userinfobot" - echo "" - read -p "Введите токен Telegram бота: " TELEGRAM_BOT_TOKEN - fi - - if [[ -z "$ADMIN_ID" ]]; then - read -p "Введите ваш Telegram ID (admin): " ADMIN_ID - fi -} - -get_controller_config() { - echo "" - print_info "Дополнительные настройки контроллера:" - read -p "Порт для API контроллера (по умолчанию 8080): " CONTROLLER_PORT - CONTROLLER_PORT=${CONTROLLER_PORT:-8080} - - read -p "Максимальное количество агентов (по умолчанию 50): " MAX_AGENTS - MAX_AGENTS=${MAX_AGENTS:-50} -} - -get_agent_config() { - if [[ -z "$CONTROLLER_URL" ]]; then - read -p "URL контроллера (например, https://controller.example.com:8080): " CONTROLLER_URL - fi - - if [[ -z "$AGENT_TOKEN" ]]; then - read -p "Токен агента (получите у администратора контроллера): " AGENT_TOKEN - fi - - read -p "Имя агента (по умолчанию: $(hostname)): " AGENT_NAME - AGENT_NAME=${AGENT_NAME:-$(hostname)} -} - -create_standalone_config() { - cat > "$CONFIG_DIR/config.yaml" < "$CONFIG_DIR/config.yaml" < "$CONFIG_DIR/config.yaml" < "/etc/systemd/system/pyguardian.service" < /dev/null; then - print_error "Python 3 is required but not installed" - exit 1 - fi - - PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') - print_success "Python version: $PYTHON_VERSION" - - # Check if Python version is >= 3.10 - if ! python3 -c 'import sys; exit(0 if sys.version_info >= (3, 10) else 1)'; then - print_error "Python 3.10+ is required, but $PYTHON_VERSION is installed" - exit 1 - fi - - # Check pip - if ! command -v pip3 &> /dev/null; then - print_error "pip3 is required but not installed" - exit 1 - fi - print_success "pip3 is available" - - # Check firewall - if command -v iptables &> /dev/null; then - print_success "iptables is available" - elif command -v nft &> /dev/null; then - print_success "nftables is available" - else - print_warning "Neither iptables nor nftables found - firewall functionality may be limited" - fi -} - -# Install system dependencies -install_dependencies() { - print_info "Установка системных зависимостей..." - - # Detect package manager - if command -v apt-get &> /dev/null; then - apt-get update - apt-get install -y python3-pip python3-venv python3-dev build-essential \ - libssl-dev libffi-dev sqlite3 curl wget systemd - print_success "Dependencies installed (APT)" - elif command -v yum &> /dev/null; then - yum install -y python3-pip python3-devel gcc openssl-devel libffi-devel \ - sqlite curl wget systemd - print_success "Dependencies installed (YUM)" - elif command -v dnf &> /dev/null; then - dnf install -y python3-pip python3-devel gcc openssl-devel libffi-devel \ - sqlite curl wget systemd - print_success "Dependencies installed (DNF)" - else - print_error "Unsupported package manager" - exit 1 - fi -} - -# Create system user -create_user() { - print_info "Создание системного пользователя..." - - if ! id "$SERVICE_USER" &>/dev/null; then - useradd --system --create-home --shell /bin/bash "$SERVICE_USER" - print_success "User $SERVICE_USER created" - else - print_info "User $SERVICE_USER already exists" - fi -} - -# Create directories -create_directories() { - print_info "Создание директорий..." - - mkdir -p "$INSTALL_DIR" "$CONFIG_DIR" "$LOG_DIR" "$DATA_DIR" - chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" "$LOG_DIR" "$DATA_DIR" - chmod 755 "$CONFIG_DIR" - chmod 750 "$LOG_DIR" "$DATA_DIR" - - print_success "Directories created" -} - -# Copy application files -copy_files() { - print_info "Копирование файлов приложения..." - - # Copy source code - cp -r "$PROJECT_DIR/src" "$INSTALL_DIR/" - cp "$PROJECT_DIR/main.py" "$INSTALL_DIR/" - cp "$PROJECT_DIR/requirements.txt" "$INSTALL_DIR/" - - # Copy configuration template - if [[ "$INSTALL_MODE" == "standalone" || "$INSTALL_MODE" == "controller" ]]; then - cp "$PROJECT_DIR/config/config.yaml" "$CONFIG_DIR/config.yaml.template" - fi - - # Set permissions - chown -R "$SERVICE_USER:$SERVICE_USER" "$INSTALL_DIR" - chmod +x "$INSTALL_DIR/main.py" - - print_success "Files copied" -} - -# Install Python dependencies -install_python_deps() { - print_info "Установка Python зависимостей..." - - # Create virtual environment - sudo -u "$SERVICE_USER" python3 -m venv "$INSTALL_DIR/venv" - - # Install dependencies - sudo -u "$SERVICE_USER" "$INSTALL_DIR/venv/bin/pip" install -r "$INSTALL_DIR/requirements.txt" - - print_success "Python dependencies installed" -} - -# Configure application based on mode -configure_application() { - print_info "Настройка приложения..." - - case "$INSTALL_MODE" in - "standalone") - configure_standalone - ;; - "controller") - configure_controller - ;; - "agent") - configure_agent - ;; - esac -} - -configure_standalone() { - print_info "Настройка автономного режима..." - - # Get configuration from user - if [[ "$NON_INTERACTIVE" != "true" ]]; then - get_telegram_config - fi - - # Create configuration file - create_standalone_config - print_success "Standalone configuration created" -} - -configure_controller() { - print_info "Настройка контроллера кластера..." - - # Get configuration from user - if [[ "$NON_INTERACTIVE" != "true" ]]; then - get_telegram_config - get_controller_config - fi - - # Create configuration file - create_controller_config - print_success "Controller configuration created" -} - -configure_agent() { - print_info "Настройка агента..." - - # Get configuration from user - if [[ "$NON_INTERACTIVE" != "true" ]]; then - get_agent_config - fi - - # Create configuration file - create_agent_config - print_success "Agent configuration created" -} - -get_telegram_config() { - if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then - echo "" - print_info "Настройка Telegram бота:" - echo "1. Создайте бота у @BotFather" - echo "2. Получите токен бота" - echo "3. Узнайте ваш chat ID у @userinfobot" - echo "" - read -p "Введите токен Telegram бота: " TELEGRAM_BOT_TOKEN - fi - - if [[ -z "$ADMIN_ID" ]]; then - read -p "Введите ваш Telegram ID (admin): " ADMIN_ID - fi -} - -get_controller_config() { - echo "" - print_info "Дополнительные настройки контроллера:" - read -p "Порт для API контроллера (по умолчанию 8080): " CONTROLLER_PORT - CONTROLLER_PORT=${CONTROLLER_PORT:-8080} - - read -p "Максимальное количество агентов (по умолчанию 50): " MAX_AGENTS - MAX_AGENTS=${MAX_AGENTS:-50} -} - -get_agent_config() { - if [[ -z "$CONTROLLER_URL" ]]; then - read -p "URL контроллера (например, https://controller.example.com:8080): " CONTROLLER_URL - fi - - if [[ -z "$AGENT_TOKEN" ]]; then - read -p "Токен агента (получите у администратора контроллера): " AGENT_TOKEN - fi - - read -p "Имя агента (по умолчанию: $(hostname)): " AGENT_NAME - AGENT_NAME=${AGENT_NAME:-$(hostname)} -} - -create_standalone_config() { - cat > "$CONFIG_DIR/config.yaml" < "$CONFIG_DIR/config.yaml" < "$CONFIG_DIR/config.yaml" < "/etc/systemd/system/pyguardian.service" </dev/null && print_info "Исправлено: $script" - fi - done - - print_success "Все скрипты исполняемы" -} - -# Test 3: Check Python requirements -test_python_requirements() { - print_test "Проверка Python требований" - - if command -v python3 &> /dev/null; then - PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') - print_success "Python version: $PYTHON_VERSION" - - if python3 -c 'import sys; exit(0 if sys.version_info >= (3, 10) else 1)'; then - print_success "Python версия соответствует требованиям (>=3.10)" - else - print_error "Python версия не соответствует требованиям (требуется >=3.10)" - return 1 - fi - else - print_error "Python3 не найден" - return 1 - fi - - if command -v pip3 &> /dev/null; then - print_success "pip3 доступен" - else - print_error "pip3 не найден" - return 1 - fi -} - -# Test 4: Check dependencies in requirements.txt -test_requirements_file() { - print_test "Проверка файла requirements.txt" - - if [[ -f "requirements.txt" ]]; then - print_success "Файл requirements.txt найден" - - local required_packages=( - "telegram" - "aiosqlite" - "pyyaml" - "cryptography" - "psutil" - ) - - for package in "${required_packages[@]}"; do - if grep -q "$package" requirements.txt; then - print_success "Зависимость найдена: $package" - else - print_error "Зависимость отсутствует: $package" - fi - done - else - print_error "Файл requirements.txt не найден" - return 1 - fi -} - -# Test 5: Check configuration files -test_config_files() { - print_test "Проверка конфигурационных файлов" - - if [[ -f "config/config.yaml" ]]; then - print_success "Основной конфиг найден: config/config.yaml" - - # Check for required sections - local sections=("telegram" "security" "firewall" "storage") - for section in "${sections[@]}"; do - if grep -q "^${section}:" config/config.yaml; then - print_success "Секция конфигурации: $section" - else - print_error "Отсутствует секция: $section" - fi - done - else - print_error "Основной конфиг не найден: config/config.yaml" - return 1 - fi - - # Check for cluster configuration - if grep -q "cluster:" config/config.yaml; then - print_success "Кластерная конфигурация найдена" - else - print_info "Кластерная конфигурация отсутствует (будет добавлена при установке)" - fi -} - -# Test 6: Check source code structure -test_source_structure() { - print_test "Проверка структуры исходного кода" - - local source_files=( - "src/storage.py" - "src/firewall.py" - "src/monitor.py" - "src/bot.py" - "src/security.py" - "src/sessions.py" - "src/password_utils.py" - "src/cluster.py" - "main.py" - ) - - for file in "${source_files[@]}"; do - if [[ -f "$file" ]]; then - print_success "Исходный файл: $file" - else - print_error "Отсутствует файл: $file" - return 1 - fi - done - - print_success "Структура исходного кода корректна" -} - -# Test 7: Check Makefile targets -test_makefile_targets() { - print_test "Проверка целей Makefile" - - if [[ -f "Makefile" ]]; then - local targets=("install" "standalone" "controller" "agent" "help" "clean") - for target in "${targets[@]}"; do - if grep -q "^${target}:" Makefile; then - print_success "Makefile цель: $target" - else - print_error "Отсутствует цель: $target" - fi - done - else - print_error "Makefile не найден" - return 1 - fi -} - -# Test 8: Validate script syntax -test_script_syntax() { - print_test "Проверка синтаксиса скриптов" - - local scripts=( - "install.sh" - "scripts/install.sh" - "scripts/docker-install.sh" - ) - - for script in "${scripts[@]}"; do - if bash -n "$script" 2>/dev/null; then - print_success "Синтаксис корректен: $script" - else - print_error "Синтаксическая ошибка в: $script" - return 1 - fi - done -} - -# Test 9: Check documentation -test_documentation() { - print_test "Проверка документации" - - local docs=( - "README.md" - "docs/INSTALLATION.md" - "docs/CLUSTER_SETUP.md" - ) - - for doc in "${docs[@]}"; do - if [[ -f "$doc" ]]; then - print_success "Документация: $doc" - else - print_error "Отсутствует документация: $doc" - fi - done -} - -# Test 10: Simulate installation steps (dry run) -test_installation_simulation() { - print_test "Симуляция процесса установки" - - # Test help output - if ./install.sh --help >/dev/null 2>&1; then - print_success "Справка install.sh работает" - else - print_error "Ошибка в справке install.sh" - fi - - # Test make help - if make help >/dev/null 2>&1; then - print_success "Справка Makefile работает" - else - print_error "Ошибка в справке Makefile" - fi - - print_success "Симуляция установки завершена" -} - -# Run all tests -run_all_tests() { - print_header - - local tests=( - "test_scripts_exist" - "test_scripts_executable" - "test_python_requirements" - "test_requirements_file" - "test_config_files" - "test_source_structure" - "test_makefile_targets" - "test_script_syntax" - "test_documentation" - "test_installation_simulation" - ) - - local passed=0 - local total=${#tests[@]} - - for test in "${tests[@]}"; do - echo "" - if $test; then - ((passed++)) - fi - done - - echo "" - echo "=================================================" - if [[ $passed -eq $total ]]; then - print_success "Все тесты пройдены: $passed/$total ✅" - echo "" - print_info "Система готова к установке!" - print_info "Используйте: sudo ./install.sh" - print_info "Или: sudo make install" - else - print_error "Тесты не пройдены: $passed/$total ❌" - echo "" - print_info "Исправьте ошибки перед установкой" - fi - echo "=================================================" -} - -# Main function -main() { - case "${1:-all}" in - "all") - run_all_tests - ;; - "scripts") - test_scripts_exist && test_scripts_executable && test_script_syntax - ;; - "python") - test_python_requirements && test_requirements_file - ;; - "config") - test_config_files - ;; - "structure") - test_source_structure - ;; - "docs") - test_documentation - ;; - *) - echo "Usage: $0 [all|scripts|python|config|structure|docs]" - echo "" - echo "Tests available:" - echo " all - Run all tests (default)" - echo " scripts - Test installation scripts" - echo " python - Test Python requirements" - echo " config - Test configuration files" - echo " structure - Test source code structure" - echo " docs - Test documentation" - ;; - esac -} - -main "$@" \ No newline at end of file diff --git a/.history/scripts/test-install_20251125204704.sh b/.history/scripts/test-install_20251125204704.sh deleted file mode 100644 index e8c97b8..0000000 --- a/.history/scripts/test-install_20251125204704.sh +++ /dev/null @@ -1,356 +0,0 @@ -#!/bin/bash - -#========================================================================== -# PyGuardian Test Script -# Демонстрация возможностей системы установки -# Author: SmartSolTech Team -#========================================================================== - -set -e - -# Colors -RED='\033[0;31m' -GREEN='\033[0;32m' -BLUE='\033[0;34m' -YELLOW='\033[1;33m' -NC='\033[0m' - -print_header() { - echo -e "${BLUE}" - echo "=================================================" - echo " PyGuardian Installation Test Suite" - echo "=================================================" - echo -e "${NC}" -} - -print_test() { - echo -e "${YELLOW}[TEST] $1${NC}" -} - -print_success() { - echo -e "${GREEN}[PASS] $1${NC}" -} - -print_info() { - echo -e "${BLUE}[INFO] $1${NC}" -} - -print_error() { - echo -e "${RED}[FAIL] $1${NC}" -} - -# Test 1: Check if all installation scripts exist -test_scripts_exist() { - print_test "Проверка существования скриптов установки" - - local scripts=( - "install.sh" - "scripts/install.sh" - "scripts/docker-install.sh" - "Makefile" - ) - - for script in "${scripts[@]}"; do - if [[ -f "$script" ]]; then - print_success "Найден: $script" - else - print_error "Отсутствует: $script" - return 1 - fi - done - - print_success "Все скрипты установки найдены" -} - -# Test 2: Check if scripts are executable -test_scripts_executable() { - print_test "Проверка прав выполнения скриптов" - - local scripts=( - "install.sh" - "scripts/install.sh" - "scripts/docker-install.sh" - ) - - for script in "${scripts[@]}"; do - if [[ -x "$script" ]]; then - print_success "Исполняемый: $script" - else - print_error "Не исполняемый: $script" - chmod +x "$script" 2>/dev/null && print_info "Исправлено: $script" - fi - done - - print_success "Все скрипты исполняемы" -} - -# Test 3: Check Python requirements -test_python_requirements() { - print_test "Проверка Python требований" - - if command -v python3 &> /dev/null; then - PYTHON_VERSION=$(python3 -c 'import sys; print(".".join(map(str, sys.version_info[:2])))') - print_success "Python version: $PYTHON_VERSION" - - if python3 -c 'import sys; exit(0 if sys.version_info >= (3, 10) else 1)'; then - print_success "Python версия соответствует требованиям (>=3.10)" - else - print_error "Python версия не соответствует требованиям (требуется >=3.10)" - return 1 - fi - else - print_error "Python3 не найден" - return 1 - fi - - if command -v pip3 &> /dev/null; then - print_success "pip3 доступен" - else - print_error "pip3 не найден" - return 1 - fi -} - -# Test 4: Check dependencies in requirements.txt -test_requirements_file() { - print_test "Проверка файла requirements.txt" - - if [[ -f "requirements.txt" ]]; then - print_success "Файл requirements.txt найден" - - local required_packages=( - "telegram" - "aiosqlite" - "pyyaml" - "cryptography" - "psutil" - ) - - for package in "${required_packages[@]}"; do - if grep -q "$package" requirements.txt; then - print_success "Зависимость найдена: $package" - else - print_error "Зависимость отсутствует: $package" - fi - done - else - print_error "Файл requirements.txt не найден" - return 1 - fi -} - -# Test 5: Check configuration files -test_config_files() { - print_test "Проверка конфигурационных файлов" - - if [[ -f "config/config.yaml" ]]; then - print_success "Основной конфиг найден: config/config.yaml" - - # Check for required sections - local sections=("telegram" "security" "firewall" "storage") - for section in "${sections[@]}"; do - if grep -q "^${section}:" config/config.yaml; then - print_success "Секция конфигурации: $section" - else - print_error "Отсутствует секция: $section" - fi - done - else - print_error "Основной конфиг не найден: config/config.yaml" - return 1 - fi - - # Check for cluster configuration - if grep -q "cluster:" config/config.yaml; then - print_success "Кластерная конфигурация найдена" - else - print_info "Кластерная конфигурация отсутствует (будет добавлена при установке)" - fi -} - -# Test 6: Check source code structure -test_source_structure() { - print_test "Проверка структуры исходного кода" - - local source_files=( - "src/storage.py" - "src/firewall.py" - "src/monitor.py" - "src/bot.py" - "src/security.py" - "src/sessions.py" - "src/password_utils.py" - "src/cluster.py" - "main.py" - ) - - for file in "${source_files[@]}"; do - if [[ -f "$file" ]]; then - print_success "Исходный файл: $file" - else - print_error "Отсутствует файл: $file" - return 1 - fi - done - - print_success "Структура исходного кода корректна" -} - -# Test 7: Check Makefile targets -test_makefile_targets() { - print_test "Проверка целей Makefile" - - if [[ -f "Makefile" ]]; then - local targets=("install" "standalone" "controller" "agent" "help" "clean") - for target in "${targets[@]}"; do - if grep -q "^${target}:" Makefile; then - print_success "Makefile цель: $target" - else - print_error "Отсутствует цель: $target" - fi - done - else - print_error "Makefile не найден" - return 1 - fi -} - -# Test 8: Validate script syntax -test_script_syntax() { - print_test "Проверка синтаксиса скриптов" - - local scripts=( - "install.sh" - "scripts/install.sh" - "scripts/docker-install.sh" - ) - - for script in "${scripts[@]}"; do - if bash -n "$script" 2>/dev/null; then - print_success "Синтаксис корректен: $script" - else - print_error "Синтаксическая ошибка в: $script" - return 1 - fi - done -} - -# Test 9: Check documentation -test_documentation() { - print_test "Проверка документации" - - local docs=( - "README.md" - "docs/INSTALLATION.md" - "docs/CLUSTER_SETUP.md" - ) - - for doc in "${docs[@]}"; do - if [[ -f "$doc" ]]; then - print_success "Документация: $doc" - else - print_error "Отсутствует документация: $doc" - fi - done -} - -# Test 10: Simulate installation steps (dry run) -test_installation_simulation() { - print_test "Симуляция процесса установки" - - # Test help output - if ./install.sh --help >/dev/null 2>&1; then - print_success "Справка install.sh работает" - else - print_error "Ошибка в справке install.sh" - fi - - # Test make help - if make help >/dev/null 2>&1; then - print_success "Справка Makefile работает" - else - print_error "Ошибка в справке Makefile" - fi - - print_success "Симуляция установки завершена" -} - -# Run all tests -run_all_tests() { - print_header - - local tests=( - "test_scripts_exist" - "test_scripts_executable" - "test_python_requirements" - "test_requirements_file" - "test_config_files" - "test_source_structure" - "test_makefile_targets" - "test_script_syntax" - "test_documentation" - "test_installation_simulation" - ) - - local passed=0 - local total=${#tests[@]} - - for test in "${tests[@]}"; do - echo "" - if $test; then - ((passed++)) - fi - done - - echo "" - echo "=================================================" - if [[ $passed -eq $total ]]; then - print_success "Все тесты пройдены: $passed/$total ✅" - echo "" - print_info "Система готова к установке!" - print_info "Используйте: sudo ./install.sh" - print_info "Или: sudo make install" - else - print_error "Тесты не пройдены: $passed/$total ❌" - echo "" - print_info "Исправьте ошибки перед установкой" - fi - echo "=================================================" -} - -# Main function -main() { - case "${1:-all}" in - "all") - run_all_tests - ;; - "scripts") - test_scripts_exist && test_scripts_executable && test_script_syntax - ;; - "python") - test_python_requirements && test_requirements_file - ;; - "config") - test_config_files - ;; - "structure") - test_source_structure - ;; - "docs") - test_documentation - ;; - *) - echo "Usage: $0 [all|scripts|python|config|structure|docs]" - echo "" - echo "Tests available:" - echo " all - Run all tests (default)" - echo " scripts - Test installation scripts" - echo " python - Test Python requirements" - echo " config - Test configuration files" - echo " structure - Test source code structure" - echo " docs - Test documentation" - ;; - esac -} - -main "$@" \ No newline at end of file diff --git a/.history/src/__init___20251125194144.py b/.history/src/__init___20251125194144.py deleted file mode 100644 index 6104dcc..0000000 --- a/.history/src/__init___20251125194144.py +++ /dev/null @@ -1 +0,0 @@ -# PyGuardian - Linux Server Protection System \ No newline at end of file diff --git a/.history/src/__init___20251125202055.py b/.history/src/__init___20251125202055.py deleted file mode 100644 index 6104dcc..0000000 --- a/.history/src/__init___20251125202055.py +++ /dev/null @@ -1 +0,0 @@ -# PyGuardian - Linux Server Protection System \ No newline at end of file diff --git a/.history/src/api_server_20251125205906.py b/.history/src/api_server_20251125205906.py deleted file mode 100644 index 7344153..0000000 --- a/.history/src/api_server_20251125205906.py +++ /dev/null @@ -1,727 +0,0 @@ -""" -API Server for PyGuardian Controller -REST API endpoints for agent authentication and cluster management -""" - -import json -import logging -import asyncio -from datetime import datetime -from typing import Dict, Any, Optional, Tuple -from aiohttp import web, WSMsgType -from aiohttp.web import Application, Request, Response, WebSocketResponse -import aiohttp_cors -import ssl -from pathlib import Path - -from .auth import AgentAuthentication, AgentAuthenticationError -from .cluster_manager import ClusterManager -from .storage import Storage - -logger = logging.getLogger(__name__) - -class PyGuardianAPI: - """ - PyGuardian Controller API Server - Provides REST API and WebSocket endpoints for agent communication - """ - - def __init__(self, cluster_manager: ClusterManager, config: Dict[str, Any]): - self.cluster_manager = cluster_manager - self.config = config - self.app = None - self.server = None - self.websockets = set() # Active WebSocket connections - - # API configuration - self.host = config.get('api_host', '0.0.0.0') - self.port = config.get('api_port', 8443) - self.ssl_cert = config.get('ssl_cert') - self.ssl_key = config.get('ssl_key') - self.api_secret = config.get('api_secret', 'change-this-secret') - - async def create_app(self) -> Application: - """Create aiohttp application with routes and middleware""" - app = web.Application() - - # Add CORS support - cors = aiohttp_cors.setup(app, defaults={ - "*": aiohttp_cors.ResourceOptions( - allow_credentials=True, - expose_headers="*", - allow_headers="*", - allow_methods="*" - ) - }) - - # Add routes - self._setup_routes(app) - - # Add CORS to all routes - for route in list(app.router.routes()): - cors.add(route) - - # Add middleware - app.middlewares.append(self._auth_middleware) - app.middlewares.append(self._error_middleware) - - self.app = app - return app - - def _setup_routes(self, app: Application): - """Setup API routes""" - - # Health check - app.router.add_get('/health', self.health_check) - - # Agent authentication endpoints - app.router.add_post('/api/v1/auth/register', self.register_agent) - app.router.add_post('/api/v1/auth/login', self.login_agent) - app.router.add_post('/api/v1/auth/refresh', self.refresh_token) - app.router.add_post('/api/v1/auth/logout', self.logout_agent) - app.router.add_post('/api/v1/auth/verify', self.verify_token) - - # Cluster management endpoints - app.router.add_get('/api/v1/cluster/status', self.cluster_status) - app.router.add_get('/api/v1/cluster/agents', self.list_agents) - app.router.add_get('/api/v1/cluster/agents/{agent_id}', self.get_agent_info) - app.router.add_post('/api/v1/cluster/agents/{agent_id}/deploy', self.deploy_agent) - app.router.add_delete('/api/v1/cluster/agents/{agent_id}', self.remove_agent) - - # Agent communication endpoints - app.router.add_post('/api/v1/agent/heartbeat', self.agent_heartbeat) - app.router.add_post('/api/v1/agent/report', self.agent_report) - app.router.add_get('/api/v1/agent/config', self.get_agent_config) - app.router.add_post('/api/v1/agent/logs', self.upload_agent_logs) - - # WebSocket endpoint for real-time communication - app.router.add_get('/ws/agent', self.websocket_handler) - - # Metrics endpoint for monitoring - app.router.add_get('/metrics', self.metrics_endpoint) - - async def _auth_middleware(self, request: Request, handler): - """Authentication middleware for protected endpoints""" - # Skip auth for health check and public endpoints - if request.path in ['/health', '/metrics'] or request.path.startswith('/api/v1/auth/'): - return await handler(request) - - # Check for API secret (for controller-to-controller communication) - api_secret = request.headers.get('X-API-Secret') - if api_secret and api_secret == self.api_secret: - request['authenticated'] = True - request['auth_type'] = 'api_secret' - return await handler(request) - - # Check for agent token - auth_header = request.headers.get('Authorization', '') - if not auth_header.startswith('Bearer '): - return web.json_response( - {'error': 'Missing or invalid authorization header'}, - status=401 - ) - - token = auth_header[7:] # Remove 'Bearer ' prefix - - try: - success, agent_id = await self.cluster_manager.verify_agent_token(token) - if success: - request['authenticated'] = True - request['auth_type'] = 'agent_token' - request['agent_id'] = agent_id - return await handler(request) - else: - return web.json_response( - {'error': 'Invalid or expired token'}, - status=401 - ) - except Exception as e: - logger.error(f"Authentication error: {e}") - return web.json_response( - {'error': 'Authentication failed'}, - status=401 - ) - - async def _error_middleware(self, request: Request, handler): - """Error handling middleware""" - try: - return await handler(request) - except web.HTTPException: - raise - except Exception as e: - logger.error(f"API error in {request.path}: {e}") - return web.json_response( - {'error': 'Internal server error'}, - status=500 - ) - - # ========================= - # API Endpoint Handlers - # ========================= - - async def health_check(self, request: Request) -> Response: - """Health check endpoint""" - return web.json_response({ - 'status': 'healthy', - 'timestamp': datetime.now().isoformat(), - 'version': '2.0.0', - 'cluster': self.cluster_manager.cluster_name - }) - - async def register_agent(self, request: Request) -> Response: - """Register new agent endpoint""" - try: - data = await request.json() - - # Validate required fields - required_fields = ['hostname', 'ip_address'] - for field in required_fields: - if field not in data: - return web.json_response( - {'error': f'Missing required field: {field}'}, - status=400 - ) - - # Register agent - success, result = await self.cluster_manager.register_new_agent( - hostname=data['hostname'], - ip_address=data['ip_address'], - ssh_user=data.get('ssh_user', 'root'), - ssh_port=data.get('ssh_port', 22), - ssh_key_path=data.get('ssh_key_path'), - ssh_password=data.get('ssh_password') - ) - - if success: - # Don't return sensitive data in logs - safe_result = {k: v for k, v in result.items() - if k not in ['secret_key']} - logger.info(f"Registered new agent: {safe_result}") - - return web.json_response(result, status=201) - else: - return web.json_response(result, status=400) - - except Exception as e: - logger.error(f"Agent registration error: {e}") - return web.json_response( - {'error': 'Registration failed'}, - status=500 - ) - - async def login_agent(self, request: Request) -> Response: - """Agent login endpoint""" - try: - data = await request.json() - - # Validate required fields - if 'agent_id' not in data or 'secret_key' not in data: - return web.json_response( - {'error': 'Missing agent_id or secret_key'}, - status=400 - ) - - client_ip = request.remote or 'unknown' - - # Authenticate agent - success, result = await self.cluster_manager.authenticate_agent( - agent_id=data['agent_id'], - secret_key=data['secret_key'], - ip_address=client_ip - ) - - if success: - logger.info(f"Agent {data['agent_id']} authenticated from {client_ip}") - return web.json_response(result) - else: - return web.json_response(result, status=401) - - except Exception as e: - logger.error(f"Agent login error: {e}") - return web.json_response( - {'error': 'Authentication failed'}, - status=500 - ) - - async def refresh_token(self, request: Request) -> Response: - """Token refresh endpoint""" - try: - data = await request.json() - - if 'refresh_token' not in data: - return web.json_response( - {'error': 'Missing refresh_token'}, - status=400 - ) - - success, result = await self.cluster_manager.refresh_agent_token( - data['refresh_token'] - ) - - if success: - return web.json_response(result) - else: - return web.json_response(result, status=401) - - except Exception as e: - logger.error(f"Token refresh error: {e}") - return web.json_response( - {'error': 'Token refresh failed'}, - status=500 - ) - - async def logout_agent(self, request: Request) -> Response: - """Agent logout endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - success = await self.cluster_manager.revoke_agent_access(agent_id) - - if success: - return web.json_response({'message': 'Logged out successfully'}) - else: - return web.json_response( - {'error': 'Logout failed'}, - status=500 - ) - - except Exception as e: - logger.error(f"Agent logout error: {e}") - return web.json_response( - {'error': 'Logout failed'}, - status=500 - ) - - async def verify_token(self, request: Request) -> Response: - """Token verification endpoint""" - try: - data = await request.json() - - if 'token' not in data: - return web.json_response( - {'error': 'Missing token'}, - status=400 - ) - - success, agent_id = await self.cluster_manager.verify_agent_token( - data['token'] - ) - - if success: - return web.json_response({ - 'valid': True, - 'agent_id': agent_id - }) - else: - return web.json_response({ - 'valid': False, - 'error': agent_id # agent_id contains error message on failure - }) - - except Exception as e: - logger.error(f"Token verification error: {e}") - return web.json_response( - {'error': 'Verification failed'}, - status=500 - ) - - async def cluster_status(self, request: Request) -> Response: - """Get cluster status""" - try: - status = await self.cluster_manager.get_cluster_stats() - auth_status = await self.cluster_manager.get_cluster_auth_status() - - return web.json_response({ - 'cluster_info': status, - 'authentication': auth_status - }) - except Exception as e: - logger.error(f"Cluster status error: {e}") - return web.json_response( - {'error': 'Failed to get cluster status'}, - status=500 - ) - - async def list_agents(self, request: Request) -> Response: - """List all agents in cluster""" - try: - agents = await self.cluster_manager.get_cluster_agents() - return web.json_response({'agents': agents}) - except Exception as e: - logger.error(f"List agents error: {e}") - return web.json_response( - {'error': 'Failed to list agents'}, - status=500 - ) - - async def get_agent_info(self, request: Request) -> Response: - """Get specific agent information""" - agent_id = request.match_info['agent_id'] - - try: - if agent_id in self.cluster_manager.agents: - agent_info = self.cluster_manager.agents[agent_id].to_dict() - - # Get additional info - auth_logs = await self.cluster_manager.get_agent_auth_logs(agent_id) - sessions = await self.cluster_manager.get_active_agent_sessions(agent_id) - - return web.json_response({ - 'agent': agent_info, - 'auth_logs': auth_logs[:10], # Last 10 logs - 'sessions': sessions - }) - else: - return web.json_response( - {'error': 'Agent not found'}, - status=404 - ) - except Exception as e: - logger.error(f"Get agent info error: {e}") - return web.json_response( - {'error': 'Failed to get agent info'}, - status=500 - ) - - async def deploy_agent(self, request: Request) -> Response: - """Deploy agent endpoint""" - agent_id = request.match_info['agent_id'] - - try: - data = await request.json() - force_reinstall = data.get('force_reinstall', False) - - success, message = await self.cluster_manager.deploy_agent( - agent_id, force_reinstall - ) - - if success: - return web.json_response({ - 'success': True, - 'message': message - }) - else: - return web.json_response({ - 'success': False, - 'message': message - }, status=400) - - except Exception as e: - logger.error(f"Deploy agent error: {e}") - return web.json_response( - {'error': 'Deployment failed'}, - status=500 - ) - - async def remove_agent(self, request: Request) -> Response: - """Remove agent endpoint""" - agent_id = request.match_info['agent_id'] - - try: - # Parse query parameters - cleanup_remote = request.query.get('cleanup_remote', 'false').lower() == 'true' - - success, message = await self.cluster_manager.remove_agent( - agent_id, cleanup_remote - ) - - if success: - return web.json_response({ - 'success': True, - 'message': message - }) - else: - return web.json_response({ - 'success': False, - 'message': message - }, status=400) - - except Exception as e: - logger.error(f"Remove agent error: {e}") - return web.json_response( - {'error': 'Agent removal failed'}, - status=500 - ) - - async def agent_heartbeat(self, request: Request) -> Response: - """Agent heartbeat endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - data = await request.json() - - # Update agent status - if agent_id in self.cluster_manager.agents: - agent = self.cluster_manager.agents[agent_id] - agent.last_check = datetime.now() - agent.status = 'online' - agent.stats.update(data.get('stats', {})) - - # Send any pending commands - return web.json_response({ - 'status': 'ok', - 'next_heartbeat': 60, # seconds - 'commands': [] # TODO: implement command queue - }) - - except Exception as e: - logger.error(f"Heartbeat error: {e}") - return web.json_response( - {'error': 'Heartbeat failed'}, - status=500 - ) - - async def agent_report(self, request: Request) -> Response: - """Agent security report endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - report = await request.json() - - # Process security report - logger.info(f"Received security report from agent {agent_id}") - - # TODO: Process and store security events - - return web.json_response({'status': 'received'}) - - except Exception as e: - logger.error(f"Agent report error: {e}") - return web.json_response( - {'error': 'Report processing failed'}, - status=500 - ) - - async def get_agent_config(self, request: Request) -> Response: - """Get agent configuration""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - # Return agent-specific configuration - config = { - 'heartbeat_interval': 60, - 'report_interval': 300, - 'log_level': 'INFO', - 'features': { - 'firewall_monitoring': True, - 'intrusion_detection': True, - 'log_analysis': True - } - } - - return web.json_response(config) - - except Exception as e: - logger.error(f"Get agent config error: {e}") - return web.json_response( - {'error': 'Config retrieval failed'}, - status=500 - ) - - async def upload_agent_logs(self, request: Request) -> Response: - """Upload agent logs endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - logs = await request.json() - - # Process and store logs - logger.info(f"Received {len(logs.get('entries', []))} log entries from agent {agent_id}") - - # TODO: Store logs in database or forward to log aggregator - - return web.json_response({'status': 'received'}) - - except Exception as e: - logger.error(f"Log upload error: {e}") - return web.json_response( - {'error': 'Log upload failed'}, - status=500 - ) - - async def websocket_handler(self, request: Request) -> WebSocketResponse: - """WebSocket endpoint for real-time agent communication""" - ws = web.WebSocketResponse() - await ws.prepare(request) - - agent_id = None - - try: - # Add to active connections - self.websockets.add(ws) - - async for msg in ws: - if msg.type == WSMsgType.TEXT: - try: - data = json.loads(msg.data) - - if data.get('type') == 'auth' and not agent_id: - # Authenticate WebSocket connection - token = data.get('token') - if token: - success, agent_id = await self.cluster_manager.verify_agent_token(token) - if success: - await ws.send_text(json.dumps({ - 'type': 'auth_success', - 'agent_id': agent_id - })) - else: - await ws.send_text(json.dumps({ - 'type': 'auth_failed', - 'error': 'Invalid token' - })) - elif agent_id: - # Handle authenticated messages - await self._handle_ws_message(ws, agent_id, data) - else: - await ws.send_text(json.dumps({ - 'type': 'error', - 'error': 'Not authenticated' - })) - - except json.JSONDecodeError: - await ws.send_text(json.dumps({ - 'type': 'error', - 'error': 'Invalid JSON' - })) - - elif msg.type == WSMsgType.ERROR: - logger.error(f'WebSocket error: {ws.exception()}') - - except Exception as e: - logger.error(f"WebSocket error: {e}") - finally: - self.websockets.discard(ws) - - return ws - - async def _handle_ws_message(self, ws: WebSocketResponse, agent_id: str, data: Dict[str, Any]): - """Handle authenticated WebSocket message""" - message_type = data.get('type') - - if message_type == 'ping': - await ws.send_text(json.dumps({'type': 'pong'})) - elif message_type == 'status_update': - # Handle agent status update - if agent_id in self.cluster_manager.agents: - agent = self.cluster_manager.agents[agent_id] - agent.status = data.get('status', 'unknown') - agent.last_check = datetime.now() - else: - await ws.send_text(json.dumps({ - 'type': 'error', - 'error': f'Unknown message type: {message_type}' - })) - - async def metrics_endpoint(self, request: Request) -> Response: - """Prometheus metrics endpoint""" - try: - metrics = [] - - # Cluster metrics - stats = await self.cluster_manager.get_cluster_stats() - auth_status = await self.cluster_manager.get_cluster_auth_status() - - metrics.append(f"pyguardian_cluster_total_agents {stats['total_agents']}") - metrics.append(f"pyguardian_cluster_online_agents {stats['online_agents']}") - metrics.append(f"pyguardian_cluster_offline_agents {stats['offline_agents']}") - metrics.append(f"pyguardian_cluster_deployed_agents {stats['deployed_agents']}") - metrics.append(f"pyguardian_cluster_authenticated_agents {auth_status['authenticated_agents']}") - metrics.append(f"pyguardian_cluster_unauthenticated_agents {auth_status['unauthenticated_agents']}") - - # WebSocket connections - metrics.append(f"pyguardian_websocket_connections {len(self.websockets)}") - - return web.Response( - text='\\n'.join(metrics), - content_type='text/plain' - ) - - except Exception as e: - logger.error(f"Metrics error: {e}") - return web.Response( - text='# Error generating metrics', - content_type='text/plain', - status=500 - ) - - # ========================= - # Server Management - # ========================= - - async def start_server(self): - """Start the API server""" - try: - app = await self.create_app() - - # Setup SSL context if certificates are provided - ssl_context = None - if self.ssl_cert and self.ssl_key: - ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_context.load_cert_chain(self.ssl_cert, self.ssl_key) - logger.info("SSL enabled for API server") - - # Start server - runner = web.AppRunner(app) - await runner.setup() - - site = web.TCPSite(runner, self.host, self.port, ssl_context=ssl_context) - await site.start() - - protocol = 'https' if ssl_context else 'http' - logger.info(f"PyGuardian API server started on {protocol}://{self.host}:{self.port}") - - self.server = runner - - except Exception as e: - logger.error(f"Failed to start API server: {e}") - raise - - async def stop_server(self): - """Stop the API server""" - if self.server: - await self.server.cleanup() - self.server = None - logger.info("API server stopped") - - async def broadcast_to_agents(self, message: Dict[str, Any]): - """Broadcast message to all connected agents via WebSocket""" - if not self.websockets: - return - - message_text = json.dumps(message) - disconnected = set() - - for ws in self.websockets: - try: - await ws.send_text(message_text) - except Exception: - disconnected.add(ws) - - # Clean up disconnected WebSockets - self.websockets -= disconnected \ No newline at end of file diff --git a/.history/src/api_server_20251125210433.py b/.history/src/api_server_20251125210433.py deleted file mode 100644 index 7344153..0000000 --- a/.history/src/api_server_20251125210433.py +++ /dev/null @@ -1,727 +0,0 @@ -""" -API Server for PyGuardian Controller -REST API endpoints for agent authentication and cluster management -""" - -import json -import logging -import asyncio -from datetime import datetime -from typing import Dict, Any, Optional, Tuple -from aiohttp import web, WSMsgType -from aiohttp.web import Application, Request, Response, WebSocketResponse -import aiohttp_cors -import ssl -from pathlib import Path - -from .auth import AgentAuthentication, AgentAuthenticationError -from .cluster_manager import ClusterManager -from .storage import Storage - -logger = logging.getLogger(__name__) - -class PyGuardianAPI: - """ - PyGuardian Controller API Server - Provides REST API and WebSocket endpoints for agent communication - """ - - def __init__(self, cluster_manager: ClusterManager, config: Dict[str, Any]): - self.cluster_manager = cluster_manager - self.config = config - self.app = None - self.server = None - self.websockets = set() # Active WebSocket connections - - # API configuration - self.host = config.get('api_host', '0.0.0.0') - self.port = config.get('api_port', 8443) - self.ssl_cert = config.get('ssl_cert') - self.ssl_key = config.get('ssl_key') - self.api_secret = config.get('api_secret', 'change-this-secret') - - async def create_app(self) -> Application: - """Create aiohttp application with routes and middleware""" - app = web.Application() - - # Add CORS support - cors = aiohttp_cors.setup(app, defaults={ - "*": aiohttp_cors.ResourceOptions( - allow_credentials=True, - expose_headers="*", - allow_headers="*", - allow_methods="*" - ) - }) - - # Add routes - self._setup_routes(app) - - # Add CORS to all routes - for route in list(app.router.routes()): - cors.add(route) - - # Add middleware - app.middlewares.append(self._auth_middleware) - app.middlewares.append(self._error_middleware) - - self.app = app - return app - - def _setup_routes(self, app: Application): - """Setup API routes""" - - # Health check - app.router.add_get('/health', self.health_check) - - # Agent authentication endpoints - app.router.add_post('/api/v1/auth/register', self.register_agent) - app.router.add_post('/api/v1/auth/login', self.login_agent) - app.router.add_post('/api/v1/auth/refresh', self.refresh_token) - app.router.add_post('/api/v1/auth/logout', self.logout_agent) - app.router.add_post('/api/v1/auth/verify', self.verify_token) - - # Cluster management endpoints - app.router.add_get('/api/v1/cluster/status', self.cluster_status) - app.router.add_get('/api/v1/cluster/agents', self.list_agents) - app.router.add_get('/api/v1/cluster/agents/{agent_id}', self.get_agent_info) - app.router.add_post('/api/v1/cluster/agents/{agent_id}/deploy', self.deploy_agent) - app.router.add_delete('/api/v1/cluster/agents/{agent_id}', self.remove_agent) - - # Agent communication endpoints - app.router.add_post('/api/v1/agent/heartbeat', self.agent_heartbeat) - app.router.add_post('/api/v1/agent/report', self.agent_report) - app.router.add_get('/api/v1/agent/config', self.get_agent_config) - app.router.add_post('/api/v1/agent/logs', self.upload_agent_logs) - - # WebSocket endpoint for real-time communication - app.router.add_get('/ws/agent', self.websocket_handler) - - # Metrics endpoint for monitoring - app.router.add_get('/metrics', self.metrics_endpoint) - - async def _auth_middleware(self, request: Request, handler): - """Authentication middleware for protected endpoints""" - # Skip auth for health check and public endpoints - if request.path in ['/health', '/metrics'] or request.path.startswith('/api/v1/auth/'): - return await handler(request) - - # Check for API secret (for controller-to-controller communication) - api_secret = request.headers.get('X-API-Secret') - if api_secret and api_secret == self.api_secret: - request['authenticated'] = True - request['auth_type'] = 'api_secret' - return await handler(request) - - # Check for agent token - auth_header = request.headers.get('Authorization', '') - if not auth_header.startswith('Bearer '): - return web.json_response( - {'error': 'Missing or invalid authorization header'}, - status=401 - ) - - token = auth_header[7:] # Remove 'Bearer ' prefix - - try: - success, agent_id = await self.cluster_manager.verify_agent_token(token) - if success: - request['authenticated'] = True - request['auth_type'] = 'agent_token' - request['agent_id'] = agent_id - return await handler(request) - else: - return web.json_response( - {'error': 'Invalid or expired token'}, - status=401 - ) - except Exception as e: - logger.error(f"Authentication error: {e}") - return web.json_response( - {'error': 'Authentication failed'}, - status=401 - ) - - async def _error_middleware(self, request: Request, handler): - """Error handling middleware""" - try: - return await handler(request) - except web.HTTPException: - raise - except Exception as e: - logger.error(f"API error in {request.path}: {e}") - return web.json_response( - {'error': 'Internal server error'}, - status=500 - ) - - # ========================= - # API Endpoint Handlers - # ========================= - - async def health_check(self, request: Request) -> Response: - """Health check endpoint""" - return web.json_response({ - 'status': 'healthy', - 'timestamp': datetime.now().isoformat(), - 'version': '2.0.0', - 'cluster': self.cluster_manager.cluster_name - }) - - async def register_agent(self, request: Request) -> Response: - """Register new agent endpoint""" - try: - data = await request.json() - - # Validate required fields - required_fields = ['hostname', 'ip_address'] - for field in required_fields: - if field not in data: - return web.json_response( - {'error': f'Missing required field: {field}'}, - status=400 - ) - - # Register agent - success, result = await self.cluster_manager.register_new_agent( - hostname=data['hostname'], - ip_address=data['ip_address'], - ssh_user=data.get('ssh_user', 'root'), - ssh_port=data.get('ssh_port', 22), - ssh_key_path=data.get('ssh_key_path'), - ssh_password=data.get('ssh_password') - ) - - if success: - # Don't return sensitive data in logs - safe_result = {k: v for k, v in result.items() - if k not in ['secret_key']} - logger.info(f"Registered new agent: {safe_result}") - - return web.json_response(result, status=201) - else: - return web.json_response(result, status=400) - - except Exception as e: - logger.error(f"Agent registration error: {e}") - return web.json_response( - {'error': 'Registration failed'}, - status=500 - ) - - async def login_agent(self, request: Request) -> Response: - """Agent login endpoint""" - try: - data = await request.json() - - # Validate required fields - if 'agent_id' not in data or 'secret_key' not in data: - return web.json_response( - {'error': 'Missing agent_id or secret_key'}, - status=400 - ) - - client_ip = request.remote or 'unknown' - - # Authenticate agent - success, result = await self.cluster_manager.authenticate_agent( - agent_id=data['agent_id'], - secret_key=data['secret_key'], - ip_address=client_ip - ) - - if success: - logger.info(f"Agent {data['agent_id']} authenticated from {client_ip}") - return web.json_response(result) - else: - return web.json_response(result, status=401) - - except Exception as e: - logger.error(f"Agent login error: {e}") - return web.json_response( - {'error': 'Authentication failed'}, - status=500 - ) - - async def refresh_token(self, request: Request) -> Response: - """Token refresh endpoint""" - try: - data = await request.json() - - if 'refresh_token' not in data: - return web.json_response( - {'error': 'Missing refresh_token'}, - status=400 - ) - - success, result = await self.cluster_manager.refresh_agent_token( - data['refresh_token'] - ) - - if success: - return web.json_response(result) - else: - return web.json_response(result, status=401) - - except Exception as e: - logger.error(f"Token refresh error: {e}") - return web.json_response( - {'error': 'Token refresh failed'}, - status=500 - ) - - async def logout_agent(self, request: Request) -> Response: - """Agent logout endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - success = await self.cluster_manager.revoke_agent_access(agent_id) - - if success: - return web.json_response({'message': 'Logged out successfully'}) - else: - return web.json_response( - {'error': 'Logout failed'}, - status=500 - ) - - except Exception as e: - logger.error(f"Agent logout error: {e}") - return web.json_response( - {'error': 'Logout failed'}, - status=500 - ) - - async def verify_token(self, request: Request) -> Response: - """Token verification endpoint""" - try: - data = await request.json() - - if 'token' not in data: - return web.json_response( - {'error': 'Missing token'}, - status=400 - ) - - success, agent_id = await self.cluster_manager.verify_agent_token( - data['token'] - ) - - if success: - return web.json_response({ - 'valid': True, - 'agent_id': agent_id - }) - else: - return web.json_response({ - 'valid': False, - 'error': agent_id # agent_id contains error message on failure - }) - - except Exception as e: - logger.error(f"Token verification error: {e}") - return web.json_response( - {'error': 'Verification failed'}, - status=500 - ) - - async def cluster_status(self, request: Request) -> Response: - """Get cluster status""" - try: - status = await self.cluster_manager.get_cluster_stats() - auth_status = await self.cluster_manager.get_cluster_auth_status() - - return web.json_response({ - 'cluster_info': status, - 'authentication': auth_status - }) - except Exception as e: - logger.error(f"Cluster status error: {e}") - return web.json_response( - {'error': 'Failed to get cluster status'}, - status=500 - ) - - async def list_agents(self, request: Request) -> Response: - """List all agents in cluster""" - try: - agents = await self.cluster_manager.get_cluster_agents() - return web.json_response({'agents': agents}) - except Exception as e: - logger.error(f"List agents error: {e}") - return web.json_response( - {'error': 'Failed to list agents'}, - status=500 - ) - - async def get_agent_info(self, request: Request) -> Response: - """Get specific agent information""" - agent_id = request.match_info['agent_id'] - - try: - if agent_id in self.cluster_manager.agents: - agent_info = self.cluster_manager.agents[agent_id].to_dict() - - # Get additional info - auth_logs = await self.cluster_manager.get_agent_auth_logs(agent_id) - sessions = await self.cluster_manager.get_active_agent_sessions(agent_id) - - return web.json_response({ - 'agent': agent_info, - 'auth_logs': auth_logs[:10], # Last 10 logs - 'sessions': sessions - }) - else: - return web.json_response( - {'error': 'Agent not found'}, - status=404 - ) - except Exception as e: - logger.error(f"Get agent info error: {e}") - return web.json_response( - {'error': 'Failed to get agent info'}, - status=500 - ) - - async def deploy_agent(self, request: Request) -> Response: - """Deploy agent endpoint""" - agent_id = request.match_info['agent_id'] - - try: - data = await request.json() - force_reinstall = data.get('force_reinstall', False) - - success, message = await self.cluster_manager.deploy_agent( - agent_id, force_reinstall - ) - - if success: - return web.json_response({ - 'success': True, - 'message': message - }) - else: - return web.json_response({ - 'success': False, - 'message': message - }, status=400) - - except Exception as e: - logger.error(f"Deploy agent error: {e}") - return web.json_response( - {'error': 'Deployment failed'}, - status=500 - ) - - async def remove_agent(self, request: Request) -> Response: - """Remove agent endpoint""" - agent_id = request.match_info['agent_id'] - - try: - # Parse query parameters - cleanup_remote = request.query.get('cleanup_remote', 'false').lower() == 'true' - - success, message = await self.cluster_manager.remove_agent( - agent_id, cleanup_remote - ) - - if success: - return web.json_response({ - 'success': True, - 'message': message - }) - else: - return web.json_response({ - 'success': False, - 'message': message - }, status=400) - - except Exception as e: - logger.error(f"Remove agent error: {e}") - return web.json_response( - {'error': 'Agent removal failed'}, - status=500 - ) - - async def agent_heartbeat(self, request: Request) -> Response: - """Agent heartbeat endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - data = await request.json() - - # Update agent status - if agent_id in self.cluster_manager.agents: - agent = self.cluster_manager.agents[agent_id] - agent.last_check = datetime.now() - agent.status = 'online' - agent.stats.update(data.get('stats', {})) - - # Send any pending commands - return web.json_response({ - 'status': 'ok', - 'next_heartbeat': 60, # seconds - 'commands': [] # TODO: implement command queue - }) - - except Exception as e: - logger.error(f"Heartbeat error: {e}") - return web.json_response( - {'error': 'Heartbeat failed'}, - status=500 - ) - - async def agent_report(self, request: Request) -> Response: - """Agent security report endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - report = await request.json() - - # Process security report - logger.info(f"Received security report from agent {agent_id}") - - # TODO: Process and store security events - - return web.json_response({'status': 'received'}) - - except Exception as e: - logger.error(f"Agent report error: {e}") - return web.json_response( - {'error': 'Report processing failed'}, - status=500 - ) - - async def get_agent_config(self, request: Request) -> Response: - """Get agent configuration""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - # Return agent-specific configuration - config = { - 'heartbeat_interval': 60, - 'report_interval': 300, - 'log_level': 'INFO', - 'features': { - 'firewall_monitoring': True, - 'intrusion_detection': True, - 'log_analysis': True - } - } - - return web.json_response(config) - - except Exception as e: - logger.error(f"Get agent config error: {e}") - return web.json_response( - {'error': 'Config retrieval failed'}, - status=500 - ) - - async def upload_agent_logs(self, request: Request) -> Response: - """Upload agent logs endpoint""" - agent_id = request.get('agent_id') - if not agent_id: - return web.json_response( - {'error': 'Agent ID not found'}, - status=400 - ) - - try: - logs = await request.json() - - # Process and store logs - logger.info(f"Received {len(logs.get('entries', []))} log entries from agent {agent_id}") - - # TODO: Store logs in database or forward to log aggregator - - return web.json_response({'status': 'received'}) - - except Exception as e: - logger.error(f"Log upload error: {e}") - return web.json_response( - {'error': 'Log upload failed'}, - status=500 - ) - - async def websocket_handler(self, request: Request) -> WebSocketResponse: - """WebSocket endpoint for real-time agent communication""" - ws = web.WebSocketResponse() - await ws.prepare(request) - - agent_id = None - - try: - # Add to active connections - self.websockets.add(ws) - - async for msg in ws: - if msg.type == WSMsgType.TEXT: - try: - data = json.loads(msg.data) - - if data.get('type') == 'auth' and not agent_id: - # Authenticate WebSocket connection - token = data.get('token') - if token: - success, agent_id = await self.cluster_manager.verify_agent_token(token) - if success: - await ws.send_text(json.dumps({ - 'type': 'auth_success', - 'agent_id': agent_id - })) - else: - await ws.send_text(json.dumps({ - 'type': 'auth_failed', - 'error': 'Invalid token' - })) - elif agent_id: - # Handle authenticated messages - await self._handle_ws_message(ws, agent_id, data) - else: - await ws.send_text(json.dumps({ - 'type': 'error', - 'error': 'Not authenticated' - })) - - except json.JSONDecodeError: - await ws.send_text(json.dumps({ - 'type': 'error', - 'error': 'Invalid JSON' - })) - - elif msg.type == WSMsgType.ERROR: - logger.error(f'WebSocket error: {ws.exception()}') - - except Exception as e: - logger.error(f"WebSocket error: {e}") - finally: - self.websockets.discard(ws) - - return ws - - async def _handle_ws_message(self, ws: WebSocketResponse, agent_id: str, data: Dict[str, Any]): - """Handle authenticated WebSocket message""" - message_type = data.get('type') - - if message_type == 'ping': - await ws.send_text(json.dumps({'type': 'pong'})) - elif message_type == 'status_update': - # Handle agent status update - if agent_id in self.cluster_manager.agents: - agent = self.cluster_manager.agents[agent_id] - agent.status = data.get('status', 'unknown') - agent.last_check = datetime.now() - else: - await ws.send_text(json.dumps({ - 'type': 'error', - 'error': f'Unknown message type: {message_type}' - })) - - async def metrics_endpoint(self, request: Request) -> Response: - """Prometheus metrics endpoint""" - try: - metrics = [] - - # Cluster metrics - stats = await self.cluster_manager.get_cluster_stats() - auth_status = await self.cluster_manager.get_cluster_auth_status() - - metrics.append(f"pyguardian_cluster_total_agents {stats['total_agents']}") - metrics.append(f"pyguardian_cluster_online_agents {stats['online_agents']}") - metrics.append(f"pyguardian_cluster_offline_agents {stats['offline_agents']}") - metrics.append(f"pyguardian_cluster_deployed_agents {stats['deployed_agents']}") - metrics.append(f"pyguardian_cluster_authenticated_agents {auth_status['authenticated_agents']}") - metrics.append(f"pyguardian_cluster_unauthenticated_agents {auth_status['unauthenticated_agents']}") - - # WebSocket connections - metrics.append(f"pyguardian_websocket_connections {len(self.websockets)}") - - return web.Response( - text='\\n'.join(metrics), - content_type='text/plain' - ) - - except Exception as e: - logger.error(f"Metrics error: {e}") - return web.Response( - text='# Error generating metrics', - content_type='text/plain', - status=500 - ) - - # ========================= - # Server Management - # ========================= - - async def start_server(self): - """Start the API server""" - try: - app = await self.create_app() - - # Setup SSL context if certificates are provided - ssl_context = None - if self.ssl_cert and self.ssl_key: - ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) - ssl_context.load_cert_chain(self.ssl_cert, self.ssl_key) - logger.info("SSL enabled for API server") - - # Start server - runner = web.AppRunner(app) - await runner.setup() - - site = web.TCPSite(runner, self.host, self.port, ssl_context=ssl_context) - await site.start() - - protocol = 'https' if ssl_context else 'http' - logger.info(f"PyGuardian API server started on {protocol}://{self.host}:{self.port}") - - self.server = runner - - except Exception as e: - logger.error(f"Failed to start API server: {e}") - raise - - async def stop_server(self): - """Stop the API server""" - if self.server: - await self.server.cleanup() - self.server = None - logger.info("API server stopped") - - async def broadcast_to_agents(self, message: Dict[str, Any]): - """Broadcast message to all connected agents via WebSocket""" - if not self.websockets: - return - - message_text = json.dumps(message) - disconnected = set() - - for ws in self.websockets: - try: - await ws.send_text(message_text) - except Exception: - disconnected.add(ws) - - # Clean up disconnected WebSockets - self.websockets -= disconnected \ No newline at end of file diff --git a/.history/src/auth_20251125205209.py b/.history/src/auth_20251125205209.py deleted file mode 100644 index 9806886..0000000 --- a/.history/src/auth_20251125205209.py +++ /dev/null @@ -1,561 +0,0 @@ -""" -Agent Authentication and Authorization Module for PyGuardian -Модуль аутентификации и авторизации агентов для PyGuardian - -Provides secure agent registration, token generation, and verification -""" - -import jwt -import hashlib -import secrets -import hmac -import uuid -import asyncio -import logging -from datetime import datetime, timedelta, timezone -from typing import Optional, Dict, Any, Tuple -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.backends import default_backend -import base64 -import os - -logger = logging.getLogger(__name__) - -class AgentAuthenticationError(Exception): - """Custom exception for authentication errors""" - pass - -class TokenExpiredError(AgentAuthenticationError): - """Raised when token has expired""" - pass - -class InvalidTokenError(AgentAuthenticationError): - """Raised when token is invalid""" - pass - -class AgentAuthentication: - """ - Agent Authentication and Authorization Manager - - Handles: - - Agent ID generation - - Secret key generation and validation - - JWT token generation and verification - - HMAC signature verification - - Secure agent registration and authentication - """ - - def __init__(self, secret_key: str, token_expiry_minutes: int = 30): - """ - Initialize authentication manager - - Args: - secret_key: Master secret key for JWT signing - token_expiry_minutes: Token expiration time in minutes - """ - self.master_secret = secret_key - self.token_expiry = token_expiry_minutes - self.algorithm = 'HS256' - - # Initialize encryption for sensitive data - self._init_encryption() - - logger.info("Agent Authentication Manager initialized") - - def _init_encryption(self): - """Initialize encryption components for sensitive data storage""" - # Derive encryption key from master secret - salt = b'pyguardian_auth_salt' # Static salt for consistency - kdf = PBKDF2HMAC( - algorithm=hashes.SHA256(), - length=32, - salt=salt, - iterations=100000, - backend=default_backend() - ) - self.encryption_key = kdf.derive(self.master_secret.encode()) - - def generate_agent_id(self, prefix: str = "agent") -> str: - """ - Generate unique Agent ID - - Args: - prefix: Prefix for agent ID (default: "agent") - - Returns: - Unique agent ID string - """ - unique_id = str(uuid.uuid4())[:8] - timestamp = datetime.now().strftime("%y%m%d") - agent_id = f"{prefix}-{timestamp}-{unique_id}" - - logger.info(f"Generated Agent ID: {agent_id}") - return agent_id - - def generate_secret_key(self, length: int = 64) -> str: - """ - Generate cryptographically secure secret key for agent - - Args: - length: Length of secret key in characters - - Returns: - Base64 encoded secret key - """ - secret_bytes = secrets.token_bytes(length // 2) - secret_key = base64.b64encode(secret_bytes).decode() - - logger.debug("Generated secret key for agent") - return secret_key - - def hash_secret_key(self, secret_key: str, salt: Optional[bytes] = None) -> Tuple[str, str]: - """ - Hash secret key for secure storage - - Args: - secret_key: Plain text secret key - salt: Optional salt for hashing (generated if None) - - Returns: - Tuple of (hashed_key, salt_b64) - """ - if salt is None: - salt = secrets.token_bytes(32) - - # Use PBKDF2 for key stretching - kdf = PBKDF2HMAC( - algorithm=hashes.SHA256(), - length=32, - salt=salt, - iterations=100000, - backend=default_backend() - ) - - hashed_key = kdf.derive(secret_key.encode()) - hashed_key_b64 = base64.b64encode(hashed_key).decode() - salt_b64 = base64.b64encode(salt).decode() - - return hashed_key_b64, salt_b64 - - def verify_secret_key(self, secret_key: str, hashed_key: str, salt_b64: str) -> bool: - """ - Verify secret key against stored hash - - Args: - secret_key: Plain text secret key to verify - hashed_key: Stored hashed key - salt_b64: Base64 encoded salt - - Returns: - True if key is valid, False otherwise - """ - try: - salt = base64.b64decode(salt_b64) - expected_hash, _ = self.hash_secret_key(secret_key, salt) - return hmac.compare_digest(expected_hash, hashed_key) - except Exception as e: - logger.error(f"Secret key verification error: {e}") - return False - - def generate_jwt_token(self, agent_id: str, additional_claims: Optional[Dict] = None) -> str: - """ - Generate JWT token for authenticated agent - - Args: - agent_id: Agent identifier - additional_claims: Additional claims to include in token - - Returns: - JWT token string - """ - now = datetime.now(timezone.utc) - expiry = now + timedelta(minutes=self.token_expiry) - - payload = { - 'agent_id': agent_id, - 'iat': now, - 'exp': expiry, - 'iss': 'pyguardian-controller', - 'aud': 'pyguardian-agent', - 'type': 'access' - } - - # Add additional claims if provided - if additional_claims: - payload.update(additional_claims) - - token = jwt.encode(payload, self.master_secret, algorithm=self.algorithm) - - logger.info(f"Generated JWT token for agent {agent_id}, expires at {expiry}") - return token - - def verify_jwt_token(self, token: str) -> Dict[str, Any]: - """ - Verify and decode JWT token - - Args: - token: JWT token to verify - - Returns: - Decoded token payload - - Raises: - TokenExpiredError: If token has expired - InvalidTokenError: If token is invalid - """ - try: - payload = jwt.decode( - token, - self.master_secret, - algorithms=[self.algorithm], - audience='pyguardian-agent', - issuer='pyguardian-controller' - ) - - logger.debug(f"Verified JWT token for agent {payload.get('agent_id')}") - return payload - - except jwt.ExpiredSignatureError: - logger.warning("JWT token has expired") - raise TokenExpiredError("Token has expired") - - except jwt.InvalidTokenError as e: - logger.error(f"Invalid JWT token: {e}") - raise InvalidTokenError(f"Invalid token: {e}") - - def generate_refresh_token(self, agent_id: str) -> str: - """ - Generate long-lived refresh token - - Args: - agent_id: Agent identifier - - Returns: - Refresh token string - """ - now = datetime.now(timezone.utc) - expiry = now + timedelta(days=30) # Refresh tokens last 30 days - - payload = { - 'agent_id': agent_id, - 'iat': now, - 'exp': expiry, - 'iss': 'pyguardian-controller', - 'aud': 'pyguardian-agent', - 'type': 'refresh' - } - - refresh_token = jwt.encode(payload, self.master_secret, algorithm=self.algorithm) - - logger.info(f"Generated refresh token for agent {agent_id}") - return refresh_token - - def refresh_access_token(self, refresh_token: str) -> str: - """ - Generate new access token using refresh token - - Args: - refresh_token: Valid refresh token - - Returns: - New access token - - Raises: - InvalidTokenError: If refresh token is invalid - """ - try: - payload = jwt.decode( - refresh_token, - self.master_secret, - algorithms=[self.algorithm], - audience='pyguardian-agent', - issuer='pyguardian-controller' - ) - - if payload.get('type') != 'refresh': - raise InvalidTokenError("Not a refresh token") - - agent_id = payload['agent_id'] - new_access_token = self.generate_jwt_token(agent_id) - - logger.info(f"Refreshed access token for agent {agent_id}") - return new_access_token - - except jwt.InvalidTokenError as e: - logger.error(f"Invalid refresh token: {e}") - raise InvalidTokenError(f"Invalid refresh token: {e}") - - def generate_hmac_signature(self, data: str, secret_key: str) -> str: - """ - Generate HMAC signature for request authentication - - Args: - data: Data to sign - secret_key: Agent's secret key - - Returns: - HMAC signature - """ - signature = hmac.new( - secret_key.encode(), - data.encode(), - hashlib.sha256 - ).hexdigest() - - return signature - - def verify_hmac_signature(self, data: str, signature: str, secret_key: str) -> bool: - """ - Verify HMAC signature - - Args: - data: Original data - signature: Provided signature - secret_key: Agent's secret key - - Returns: - True if signature is valid - """ - expected_signature = self.generate_hmac_signature(data, secret_key) - return hmac.compare_digest(signature, expected_signature) - - def encrypt_sensitive_data(self, data: str) -> str: - """ - Encrypt sensitive data for storage - - Args: - data: Plain text data to encrypt - - Returns: - Base64 encoded encrypted data - """ - # Generate random IV - iv = os.urandom(16) - - # Create cipher - cipher = Cipher( - algorithms.AES(self.encryption_key), - modes.CBC(iv), - backend=default_backend() - ) - - encryptor = cipher.encryptor() - - # Pad data to block size - padded_data = self._pad_data(data.encode()) - - # Encrypt - encrypted = encryptor.update(padded_data) + encryptor.finalize() - - # Combine IV and encrypted data - encrypted_with_iv = iv + encrypted - - return base64.b64encode(encrypted_with_iv).decode() - - def decrypt_sensitive_data(self, encrypted_data: str) -> str: - """ - Decrypt sensitive data from storage - - Args: - encrypted_data: Base64 encoded encrypted data - - Returns: - Decrypted plain text data - """ - try: - # Decode from base64 - encrypted_with_iv = base64.b64decode(encrypted_data) - - # Extract IV and encrypted data - iv = encrypted_with_iv[:16] - encrypted = encrypted_with_iv[16:] - - # Create cipher - cipher = Cipher( - algorithms.AES(self.encryption_key), - modes.CBC(iv), - backend=default_backend() - ) - - decryptor = cipher.decryptor() - - # Decrypt - padded_data = decryptor.update(encrypted) + decryptor.finalize() - - # Remove padding - data = self._unpad_data(padded_data) - - return data.decode() - - except Exception as e: - logger.error(f"Decryption error: {e}") - raise AgentAuthenticationError(f"Failed to decrypt data: {e}") - - def _pad_data(self, data: bytes) -> bytes: - """Add PKCS7 padding to data""" - pad_length = 16 - (len(data) % 16) - return data + bytes([pad_length]) * pad_length - - def _unpad_data(self, padded_data: bytes) -> bytes: - """Remove PKCS7 padding from data""" - pad_length = padded_data[-1] - return padded_data[:-pad_length] - - def create_agent_credentials(self, agent_id: Optional[str] = None) -> Dict[str, str]: - """ - Create complete set of credentials for new agent - - Args: - agent_id: Optional agent ID (generated if not provided) - - Returns: - Dictionary with agent credentials - """ - if not agent_id: - agent_id = self.generate_agent_id() - - secret_key = self.generate_secret_key() - hashed_key, salt = self.hash_secret_key(secret_key) - access_token = self.generate_jwt_token(agent_id) - refresh_token = self.generate_refresh_token(agent_id) - - credentials = { - 'agent_id': agent_id, - 'secret_key': secret_key, - 'hashed_key': hashed_key, - 'salt': salt, - 'access_token': access_token, - 'refresh_token': refresh_token, - 'created_at': datetime.now(timezone.utc).isoformat() - } - - logger.info(f"Created complete credentials for agent {agent_id}") - return credentials - - async def authenticate_agent(self, agent_id: str, secret_key: str, - stored_hash: str, salt: str) -> Dict[str, str]: - """ - Authenticate agent and generate tokens - - Args: - agent_id: Agent identifier - secret_key: Agent's secret key - stored_hash: Stored hashed secret key - salt: Salt used for hashing - - Returns: - Dictionary with authentication result and tokens - - Raises: - AgentAuthenticationError: If authentication fails - """ - # Verify secret key - if not self.verify_secret_key(secret_key, stored_hash, salt): - logger.warning(f"Authentication failed for agent {agent_id}") - raise AgentAuthenticationError("Invalid credentials") - - # Generate tokens - access_token = self.generate_jwt_token(agent_id) - refresh_token = self.generate_refresh_token(agent_id) - - result = { - 'status': 'authenticated', - 'agent_id': agent_id, - 'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': self.token_expiry * 60, # in seconds - 'token_type': 'Bearer' - } - - logger.info(f"Successfully authenticated agent {agent_id}") - return result - - def validate_agent_request(self, token: str, expected_agent_id: Optional[str] = None) -> str: - """ - Validate agent request token and return agent ID - - Args: - token: JWT token from request - expected_agent_id: Optional expected agent ID for validation - - Returns: - Agent ID from validated token - - Raises: - AgentAuthenticationError: If validation fails - """ - try: - payload = self.verify_jwt_token(token) - agent_id = payload['agent_id'] - - if expected_agent_id and agent_id != expected_agent_id: - raise AgentAuthenticationError("Agent ID mismatch") - - return agent_id - - except (TokenExpiredError, InvalidTokenError) as e: - raise AgentAuthenticationError(str(e)) - - -class AgentSession: - """ - Manage agent session state and metadata - """ - - def __init__(self, agent_id: str, ip_address: str): - self.agent_id = agent_id - self.ip_address = ip_address - self.created_at = datetime.now(timezone.utc) - self.last_seen = self.created_at - self.is_active = True - self.requests_count = 0 - self.last_activity = None - - def update_activity(self, activity: str): - """Update session activity""" - self.last_seen = datetime.now(timezone.utc) - self.last_activity = activity - self.requests_count += 1 - - def is_expired(self, timeout_minutes: int = 60) -> bool: - """Check if session has expired""" - if not self.is_active: - return True - - timeout = timedelta(minutes=timeout_minutes) - return datetime.now(timezone.utc) - self.last_seen > timeout - - def deactivate(self): - """Deactivate session""" - self.is_active = False - - def to_dict(self) -> Dict[str, Any]: - """Convert session to dictionary""" - return { - 'agent_id': self.agent_id, - 'ip_address': self.ip_address, - 'created_at': self.created_at.isoformat(), - 'last_seen': self.last_seen.isoformat(), - 'is_active': self.is_active, - 'requests_count': self.requests_count, - 'last_activity': self.last_activity - } - - -# Global instance for easy access -_auth_manager: Optional[AgentAuthentication] = None - -def get_auth_manager(secret_key: str) -> AgentAuthentication: - """Get singleton instance of authentication manager""" - global _auth_manager - if _auth_manager is None: - _auth_manager = AgentAuthentication(secret_key) - return _auth_manager - -def init_auth_manager(secret_key: str, token_expiry: int = 30): - """Initialize global authentication manager""" - global _auth_manager - _auth_manager = AgentAuthentication(secret_key, token_expiry) - return _auth_manager \ No newline at end of file diff --git a/.history/src/auth_20251125210433.py b/.history/src/auth_20251125210433.py deleted file mode 100644 index 9806886..0000000 --- a/.history/src/auth_20251125210433.py +++ /dev/null @@ -1,561 +0,0 @@ -""" -Agent Authentication and Authorization Module for PyGuardian -Модуль аутентификации и авторизации агентов для PyGuardian - -Provides secure agent registration, token generation, and verification -""" - -import jwt -import hashlib -import secrets -import hmac -import uuid -import asyncio -import logging -from datetime import datetime, timedelta, timezone -from typing import Optional, Dict, Any, Tuple -from cryptography.hazmat.primitives import hashes -from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC -from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes -from cryptography.hazmat.backends import default_backend -import base64 -import os - -logger = logging.getLogger(__name__) - -class AgentAuthenticationError(Exception): - """Custom exception for authentication errors""" - pass - -class TokenExpiredError(AgentAuthenticationError): - """Raised when token has expired""" - pass - -class InvalidTokenError(AgentAuthenticationError): - """Raised when token is invalid""" - pass - -class AgentAuthentication: - """ - Agent Authentication and Authorization Manager - - Handles: - - Agent ID generation - - Secret key generation and validation - - JWT token generation and verification - - HMAC signature verification - - Secure agent registration and authentication - """ - - def __init__(self, secret_key: str, token_expiry_minutes: int = 30): - """ - Initialize authentication manager - - Args: - secret_key: Master secret key for JWT signing - token_expiry_minutes: Token expiration time in minutes - """ - self.master_secret = secret_key - self.token_expiry = token_expiry_minutes - self.algorithm = 'HS256' - - # Initialize encryption for sensitive data - self._init_encryption() - - logger.info("Agent Authentication Manager initialized") - - def _init_encryption(self): - """Initialize encryption components for sensitive data storage""" - # Derive encryption key from master secret - salt = b'pyguardian_auth_salt' # Static salt for consistency - kdf = PBKDF2HMAC( - algorithm=hashes.SHA256(), - length=32, - salt=salt, - iterations=100000, - backend=default_backend() - ) - self.encryption_key = kdf.derive(self.master_secret.encode()) - - def generate_agent_id(self, prefix: str = "agent") -> str: - """ - Generate unique Agent ID - - Args: - prefix: Prefix for agent ID (default: "agent") - - Returns: - Unique agent ID string - """ - unique_id = str(uuid.uuid4())[:8] - timestamp = datetime.now().strftime("%y%m%d") - agent_id = f"{prefix}-{timestamp}-{unique_id}" - - logger.info(f"Generated Agent ID: {agent_id}") - return agent_id - - def generate_secret_key(self, length: int = 64) -> str: - """ - Generate cryptographically secure secret key for agent - - Args: - length: Length of secret key in characters - - Returns: - Base64 encoded secret key - """ - secret_bytes = secrets.token_bytes(length // 2) - secret_key = base64.b64encode(secret_bytes).decode() - - logger.debug("Generated secret key for agent") - return secret_key - - def hash_secret_key(self, secret_key: str, salt: Optional[bytes] = None) -> Tuple[str, str]: - """ - Hash secret key for secure storage - - Args: - secret_key: Plain text secret key - salt: Optional salt for hashing (generated if None) - - Returns: - Tuple of (hashed_key, salt_b64) - """ - if salt is None: - salt = secrets.token_bytes(32) - - # Use PBKDF2 for key stretching - kdf = PBKDF2HMAC( - algorithm=hashes.SHA256(), - length=32, - salt=salt, - iterations=100000, - backend=default_backend() - ) - - hashed_key = kdf.derive(secret_key.encode()) - hashed_key_b64 = base64.b64encode(hashed_key).decode() - salt_b64 = base64.b64encode(salt).decode() - - return hashed_key_b64, salt_b64 - - def verify_secret_key(self, secret_key: str, hashed_key: str, salt_b64: str) -> bool: - """ - Verify secret key against stored hash - - Args: - secret_key: Plain text secret key to verify - hashed_key: Stored hashed key - salt_b64: Base64 encoded salt - - Returns: - True if key is valid, False otherwise - """ - try: - salt = base64.b64decode(salt_b64) - expected_hash, _ = self.hash_secret_key(secret_key, salt) - return hmac.compare_digest(expected_hash, hashed_key) - except Exception as e: - logger.error(f"Secret key verification error: {e}") - return False - - def generate_jwt_token(self, agent_id: str, additional_claims: Optional[Dict] = None) -> str: - """ - Generate JWT token for authenticated agent - - Args: - agent_id: Agent identifier - additional_claims: Additional claims to include in token - - Returns: - JWT token string - """ - now = datetime.now(timezone.utc) - expiry = now + timedelta(minutes=self.token_expiry) - - payload = { - 'agent_id': agent_id, - 'iat': now, - 'exp': expiry, - 'iss': 'pyguardian-controller', - 'aud': 'pyguardian-agent', - 'type': 'access' - } - - # Add additional claims if provided - if additional_claims: - payload.update(additional_claims) - - token = jwt.encode(payload, self.master_secret, algorithm=self.algorithm) - - logger.info(f"Generated JWT token for agent {agent_id}, expires at {expiry}") - return token - - def verify_jwt_token(self, token: str) -> Dict[str, Any]: - """ - Verify and decode JWT token - - Args: - token: JWT token to verify - - Returns: - Decoded token payload - - Raises: - TokenExpiredError: If token has expired - InvalidTokenError: If token is invalid - """ - try: - payload = jwt.decode( - token, - self.master_secret, - algorithms=[self.algorithm], - audience='pyguardian-agent', - issuer='pyguardian-controller' - ) - - logger.debug(f"Verified JWT token for agent {payload.get('agent_id')}") - return payload - - except jwt.ExpiredSignatureError: - logger.warning("JWT token has expired") - raise TokenExpiredError("Token has expired") - - except jwt.InvalidTokenError as e: - logger.error(f"Invalid JWT token: {e}") - raise InvalidTokenError(f"Invalid token: {e}") - - def generate_refresh_token(self, agent_id: str) -> str: - """ - Generate long-lived refresh token - - Args: - agent_id: Agent identifier - - Returns: - Refresh token string - """ - now = datetime.now(timezone.utc) - expiry = now + timedelta(days=30) # Refresh tokens last 30 days - - payload = { - 'agent_id': agent_id, - 'iat': now, - 'exp': expiry, - 'iss': 'pyguardian-controller', - 'aud': 'pyguardian-agent', - 'type': 'refresh' - } - - refresh_token = jwt.encode(payload, self.master_secret, algorithm=self.algorithm) - - logger.info(f"Generated refresh token for agent {agent_id}") - return refresh_token - - def refresh_access_token(self, refresh_token: str) -> str: - """ - Generate new access token using refresh token - - Args: - refresh_token: Valid refresh token - - Returns: - New access token - - Raises: - InvalidTokenError: If refresh token is invalid - """ - try: - payload = jwt.decode( - refresh_token, - self.master_secret, - algorithms=[self.algorithm], - audience='pyguardian-agent', - issuer='pyguardian-controller' - ) - - if payload.get('type') != 'refresh': - raise InvalidTokenError("Not a refresh token") - - agent_id = payload['agent_id'] - new_access_token = self.generate_jwt_token(agent_id) - - logger.info(f"Refreshed access token for agent {agent_id}") - return new_access_token - - except jwt.InvalidTokenError as e: - logger.error(f"Invalid refresh token: {e}") - raise InvalidTokenError(f"Invalid refresh token: {e}") - - def generate_hmac_signature(self, data: str, secret_key: str) -> str: - """ - Generate HMAC signature for request authentication - - Args: - data: Data to sign - secret_key: Agent's secret key - - Returns: - HMAC signature - """ - signature = hmac.new( - secret_key.encode(), - data.encode(), - hashlib.sha256 - ).hexdigest() - - return signature - - def verify_hmac_signature(self, data: str, signature: str, secret_key: str) -> bool: - """ - Verify HMAC signature - - Args: - data: Original data - signature: Provided signature - secret_key: Agent's secret key - - Returns: - True if signature is valid - """ - expected_signature = self.generate_hmac_signature(data, secret_key) - return hmac.compare_digest(signature, expected_signature) - - def encrypt_sensitive_data(self, data: str) -> str: - """ - Encrypt sensitive data for storage - - Args: - data: Plain text data to encrypt - - Returns: - Base64 encoded encrypted data - """ - # Generate random IV - iv = os.urandom(16) - - # Create cipher - cipher = Cipher( - algorithms.AES(self.encryption_key), - modes.CBC(iv), - backend=default_backend() - ) - - encryptor = cipher.encryptor() - - # Pad data to block size - padded_data = self._pad_data(data.encode()) - - # Encrypt - encrypted = encryptor.update(padded_data) + encryptor.finalize() - - # Combine IV and encrypted data - encrypted_with_iv = iv + encrypted - - return base64.b64encode(encrypted_with_iv).decode() - - def decrypt_sensitive_data(self, encrypted_data: str) -> str: - """ - Decrypt sensitive data from storage - - Args: - encrypted_data: Base64 encoded encrypted data - - Returns: - Decrypted plain text data - """ - try: - # Decode from base64 - encrypted_with_iv = base64.b64decode(encrypted_data) - - # Extract IV and encrypted data - iv = encrypted_with_iv[:16] - encrypted = encrypted_with_iv[16:] - - # Create cipher - cipher = Cipher( - algorithms.AES(self.encryption_key), - modes.CBC(iv), - backend=default_backend() - ) - - decryptor = cipher.decryptor() - - # Decrypt - padded_data = decryptor.update(encrypted) + decryptor.finalize() - - # Remove padding - data = self._unpad_data(padded_data) - - return data.decode() - - except Exception as e: - logger.error(f"Decryption error: {e}") - raise AgentAuthenticationError(f"Failed to decrypt data: {e}") - - def _pad_data(self, data: bytes) -> bytes: - """Add PKCS7 padding to data""" - pad_length = 16 - (len(data) % 16) - return data + bytes([pad_length]) * pad_length - - def _unpad_data(self, padded_data: bytes) -> bytes: - """Remove PKCS7 padding from data""" - pad_length = padded_data[-1] - return padded_data[:-pad_length] - - def create_agent_credentials(self, agent_id: Optional[str] = None) -> Dict[str, str]: - """ - Create complete set of credentials for new agent - - Args: - agent_id: Optional agent ID (generated if not provided) - - Returns: - Dictionary with agent credentials - """ - if not agent_id: - agent_id = self.generate_agent_id() - - secret_key = self.generate_secret_key() - hashed_key, salt = self.hash_secret_key(secret_key) - access_token = self.generate_jwt_token(agent_id) - refresh_token = self.generate_refresh_token(agent_id) - - credentials = { - 'agent_id': agent_id, - 'secret_key': secret_key, - 'hashed_key': hashed_key, - 'salt': salt, - 'access_token': access_token, - 'refresh_token': refresh_token, - 'created_at': datetime.now(timezone.utc).isoformat() - } - - logger.info(f"Created complete credentials for agent {agent_id}") - return credentials - - async def authenticate_agent(self, agent_id: str, secret_key: str, - stored_hash: str, salt: str) -> Dict[str, str]: - """ - Authenticate agent and generate tokens - - Args: - agent_id: Agent identifier - secret_key: Agent's secret key - stored_hash: Stored hashed secret key - salt: Salt used for hashing - - Returns: - Dictionary with authentication result and tokens - - Raises: - AgentAuthenticationError: If authentication fails - """ - # Verify secret key - if not self.verify_secret_key(secret_key, stored_hash, salt): - logger.warning(f"Authentication failed for agent {agent_id}") - raise AgentAuthenticationError("Invalid credentials") - - # Generate tokens - access_token = self.generate_jwt_token(agent_id) - refresh_token = self.generate_refresh_token(agent_id) - - result = { - 'status': 'authenticated', - 'agent_id': agent_id, - 'access_token': access_token, - 'refresh_token': refresh_token, - 'expires_in': self.token_expiry * 60, # in seconds - 'token_type': 'Bearer' - } - - logger.info(f"Successfully authenticated agent {agent_id}") - return result - - def validate_agent_request(self, token: str, expected_agent_id: Optional[str] = None) -> str: - """ - Validate agent request token and return agent ID - - Args: - token: JWT token from request - expected_agent_id: Optional expected agent ID for validation - - Returns: - Agent ID from validated token - - Raises: - AgentAuthenticationError: If validation fails - """ - try: - payload = self.verify_jwt_token(token) - agent_id = payload['agent_id'] - - if expected_agent_id and agent_id != expected_agent_id: - raise AgentAuthenticationError("Agent ID mismatch") - - return agent_id - - except (TokenExpiredError, InvalidTokenError) as e: - raise AgentAuthenticationError(str(e)) - - -class AgentSession: - """ - Manage agent session state and metadata - """ - - def __init__(self, agent_id: str, ip_address: str): - self.agent_id = agent_id - self.ip_address = ip_address - self.created_at = datetime.now(timezone.utc) - self.last_seen = self.created_at - self.is_active = True - self.requests_count = 0 - self.last_activity = None - - def update_activity(self, activity: str): - """Update session activity""" - self.last_seen = datetime.now(timezone.utc) - self.last_activity = activity - self.requests_count += 1 - - def is_expired(self, timeout_minutes: int = 60) -> bool: - """Check if session has expired""" - if not self.is_active: - return True - - timeout = timedelta(minutes=timeout_minutes) - return datetime.now(timezone.utc) - self.last_seen > timeout - - def deactivate(self): - """Deactivate session""" - self.is_active = False - - def to_dict(self) -> Dict[str, Any]: - """Convert session to dictionary""" - return { - 'agent_id': self.agent_id, - 'ip_address': self.ip_address, - 'created_at': self.created_at.isoformat(), - 'last_seen': self.last_seen.isoformat(), - 'is_active': self.is_active, - 'requests_count': self.requests_count, - 'last_activity': self.last_activity - } - - -# Global instance for easy access -_auth_manager: Optional[AgentAuthentication] = None - -def get_auth_manager(secret_key: str) -> AgentAuthentication: - """Get singleton instance of authentication manager""" - global _auth_manager - if _auth_manager is None: - _auth_manager = AgentAuthentication(secret_key) - return _auth_manager - -def init_auth_manager(secret_key: str, token_expiry: int = 30): - """Initialize global authentication manager""" - global _auth_manager - _auth_manager = AgentAuthentication(secret_key, token_expiry) - return _auth_manager \ No newline at end of file diff --git a/.history/src/bot_20251125194854.py b/.history/src/bot_20251125194854.py deleted file mode 100644 index 818d99f..0000000 --- a/.history/src/bot_20251125194854.py +++ /dev/null @@ -1,632 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200119.py b/.history/src/bot_20251125200119.py deleted file mode 100644 index 4b7e75f..0000000 --- a/.history/src/bot_20251125200119.py +++ /dev/null @@ -1,653 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200506.py b/.history/src/bot_20251125200506.py deleted file mode 100644 index 07bacd0..0000000 --- a/.history/src/bot_20251125200506.py +++ /dev/null @@ -1,955 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") - - async def compromises_command(self, update: Update, context): - """GET compromises Показать список обнаруженных компромиссов""" - if not await self._check_access(update): - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context): - """GET sessions Показать активные SSH сессии""" - if not await self._check_access(update): - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context): - """KICK user/pid Завершить сессию пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context): - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context): - """SET PASSWORD Установить пароль для пользователя""" - if not await self._check_access(update): - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200603.py b/.history/src/bot_20251125200603.py deleted file mode 100644 index 4b7e75f..0000000 --- a/.history/src/bot_20251125200603.py +++ /dev/null @@ -1,653 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200645.py b/.history/src/bot_20251125200645.py deleted file mode 100644 index cbc7494..0000000 --- a/.history/src/bot_20251125200645.py +++ /dev/null @@ -1,955 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not await self._check_access(update): - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not await self._check_access(update): - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not await self._check_access(update): - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200716.py b/.history/src/bot_20251125200716.py deleted file mode 100644 index f315ca9..0000000 --- a/.history/src/bot_20251125200716.py +++ /dev/null @@ -1,956 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not await self._check_access(update): - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not await self._check_access(update): - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200722.py b/.history/src/bot_20251125200722.py deleted file mode 100644 index 71940e9..0000000 --- a/.history/src/bot_20251125200722.py +++ /dev/null @@ -1,957 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not await self._check_access(update): - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200728.py b/.history/src/bot_20251125200728.py deleted file mode 100644 index dedc306..0000000 --- a/.history/src/bot_20251125200728.py +++ /dev/null @@ -1,958 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not await self._check_access(update): - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not await self._check_access(update): - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200735.py b/.history/src/bot_20251125200735.py deleted file mode 100644 index 32213fa..0000000 --- a/.history/src/bot_20251125200735.py +++ /dev/null @@ -1,959 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not await self._check_access(update): - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125200742.py b/.history/src/bot_20251125200742.py deleted file mode 100644 index 3f82ac6..0000000 --- a/.history/src/bot_20251125200742.py +++ /dev/null @@ -1,960 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125202055.py b/.history/src/bot_20251125202055.py deleted file mode 100644 index 3f82ac6..0000000 --- a/.history/src/bot_20251125202055.py +++ /dev/null @@ -1,960 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125202324.py b/.history/src/bot_20251125202324.py deleted file mode 100644 index 3960a3a..0000000 --- a/.history/src/bot_20251125202324.py +++ /dev/null @@ -1,969 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None, cluster_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - self.cluster_manager = cluster_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Новые команды управления кластером - self.application.add_handler(CommandHandler("cluster", self.cluster_command)) - self.application.add_handler(CommandHandler("add_server", self.add_server_command)) - self.application.add_handler(CommandHandler("remove_server", self.remove_server_command)) - self.application.add_handler(CommandHandler("deploy_agent", self.deploy_agent_command)) - self.application.add_handler(CommandHandler("agents", self.agents_command)) - self.application.add_handler(CommandHandler("check_agents", self.check_agents_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125202424.py b/.history/src/bot_20251125202424.py deleted file mode 100644 index 029d62a..0000000 --- a/.history/src/bot_20251125202424.py +++ /dev/null @@ -1,1336 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None, cluster_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - self.cluster_manager = cluster_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Новые команды управления кластером - self.application.add_handler(CommandHandler("cluster", self.cluster_command)) - self.application.add_handler(CommandHandler("add_server", self.add_server_command)) - self.application.add_handler(CommandHandler("remove_server", self.remove_server_command)) - self.application.add_handler(CommandHandler("deploy_agent", self.deploy_agent_command)) - self.application.add_handler(CommandHandler("agents", self.agents_command)) - self.application.add_handler(CommandHandler("check_agents", self.check_agents_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - # === CLUSTER MANAGEMENT COMMANDS === - - async def cluster_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """CLUSTER info Показать информацию о кластере""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - # Получаем статистику кластера - cluster_stats = await self.cluster_manager.get_cluster_stats() - agents_list = await self.cluster_manager.list_agents() - - response = f"🏢 *Кластер {cluster_stats['cluster_name']}*\n\n" - response += f"📊 **Статистика:**\n" - response += f" • Всего агентов: `{cluster_stats['total_agents']}`\n" - response += f" • Онлайн: `{cluster_stats['online_agents']}`\n" - response += f" • Оффлайн: `{cluster_stats['offline_agents']}`\n" - response += f" • Развернуто: `{cluster_stats['deployed_agents']}`\n\n" - - if agents_list: - response += "🖥️ **Агенты:**\n" - for agent in agents_list[:5]: # Показываем первые 5 - status_emoji = "🟢" if agent['status'] == 'online' else "🔴" if agent['status'] == 'offline' else "🟡" - response += f"{status_emoji} `{agent['hostname']}` ({agent['ip_address']})\n" - - if len(agents_list) > 5: - response += f"\n... и еще {len(agents_list) - 5} агентов\n" - else: - response += "📝 Агенты не добавлены\n" - - response += f"\n🕐 Последнее обновление: `{cluster_stats['last_updated'][:19]}`" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /cluster: {e}") - - async def add_server_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """ADD SERVER Добавить новый сервер в кластер""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/add_server [ssh_user] [ssh_port]`\n\n" - "Примеры:\n" - "`/add_server web-01 192.168.1.100`\n" - "`/add_server db-server 10.0.0.50 ubuntu 2222`", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - hostname = context.args[0] - ip_address = context.args[1] - ssh_user = context.args[2] if len(context.args) > 2 else 'root' - ssh_port = int(context.args[3]) if len(context.args) > 3 else 22 - - # Добавляем сервер - success, message = await self.cluster_manager.add_agent( - hostname=hostname, - ip_address=ip_address, - ssh_user=ssh_user, - ssh_port=ssh_port - ) - - if success: - response = f"✅ *Сервер добавлен в кластер*\n\n" - response += f"🖥️ **Hostname:** `{hostname}`\n" - response += f"🌐 **IP:** `{ip_address}`\n" - response += f"👤 **SSH User:** `{ssh_user}`\n" - response += f"🔌 **SSH Port:** `{ssh_port}`\n\n" - response += f"ℹ️ {message}\n\n" - response += "**Следующие шаги:**\n" - response += f"1. `/deploy_agent {self.cluster_manager.generate_agent_id(hostname, ip_address)}` - развернуть агент\n" - response += f"2. `/check_agents` - проверить статус" - - # Логируем действие - logger.info(f"Сервер {hostname} ({ip_address}) добавлен в кластер через Telegram") - else: - response = f"❌ *Не удалось добавить сервер*\n\n" - response += f"🖥️ **Hostname:** `{hostname}`\n" - response += f"🌐 **IP:** `{ip_address}`\n\n" - response += f"**Ошибка:** {message}" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /add_server: {e}") - - async def remove_server_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """REMOVE SERVER Удалить сервер из кластера""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/remove_server [cleanup]`\n\n" - "Примеры:\n" - "`/remove_server web-01-192-168-1-100`\n" - "`/remove_server web-01-192-168-1-100 cleanup` - с очисткой на сервере", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agent_id = context.args[0] - cleanup_remote = len(context.args) > 1 and context.args[1].lower() == 'cleanup' - - # Удаляем агент - success, message = await self.cluster_manager.remove_agent(agent_id, cleanup_remote) - - if success: - response = f"✅ *Сервер удален из кластера*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"🗑️ **Очистка на сервере:** {'Да' if cleanup_remote else 'Нет'}\n\n" - response += f"ℹ️ {message}" - - # Логируем действие - logger.info(f"Агент {agent_id} удален из кластера через Telegram") - else: - response = f"❌ *Не удалось удалить сервер*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n\n" - response += f"**Ошибка:** {message}" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /remove_server: {e}") - - async def deploy_agent_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """DEPLOY AGENT Развернуть PyGuardian агент на сервере""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/deploy_agent [force]`\n\n" - "Примеры:\n" - "`/deploy_agent web-01-192-168-1-100`\n" - "`/deploy_agent web-01-192-168-1-100 force` - принудительная переустановка", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agent_id = context.args[0] - force_reinstall = len(context.args) > 1 and context.args[1].lower() == 'force' - - # Отправляем начальное сообщение - status_message = await update.message.reply_text( - f"🚀 *Начинаю развертывание...*\n\n" - f"🖥️ **Agent ID:** `{agent_id}`\n" - f"⚙️ **Режим:** {'Переустановка' if force_reinstall else 'Установка'}\n\n" - f"⏳ Подключение к серверу...", - parse_mode='Markdown' - ) - - # Развертываем агент - success, message = await self.cluster_manager.deploy_agent(agent_id, force_reinstall) - - if success: - response = f"✅ *Развертывание завершено*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"🎉 **Результат:** Успешно\n\n" - response += f"ℹ️ {message}\n\n" - response += "**Следующие шаги:**\n" - response += f"• `/check_agents` - проверить статус\n" - response += f"• `/cluster` - просмотр кластера" - - # Логируем действие - logger.info(f"PyGuardian развернут на агенте {agent_id} через Telegram") - else: - response = f"❌ *Развертывание не удалось*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"💥 **Ошибка:** {message}\n\n" - response += "**Возможные решения:**\n" - response += "• Проверьте SSH соединение\n" - response += "• Убедитесь в наличии прав root\n" - response += "• Проверьте интернет соединение на сервере" - - # Обновляем сообщение - await context.bot.edit_message_text( - text=response, - chat_id=status_message.chat_id, - message_id=status_message.message_id, - parse_mode='Markdown' - ) - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /deploy_agent: {e}") - - async def agents_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """AGENTS list Показать список всех агентов кластера""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agents_list = await self.cluster_manager.list_agents() - - if not agents_list: - await update.message.reply_text( - "📝 *Агенты кластера*\n\n" - "Список пуст. Добавьте серверы командой `/add_server`", - parse_mode='Markdown' - ) - return - - response = f"🖥️ *Агенты кластера* ({len(agents_list)})\n\n" - - for agent in agents_list: - status_emoji = { - 'online': '🟢', - 'offline': '🔴', - 'deployed': '🟡', - 'added': '⚪' - }.get(agent['status'], '❓') - - response += f"{status_emoji} **{agent['hostname']}**\n" - response += f" • IP: `{agent['ip_address']}`\n" - response += f" • ID: `{agent['agent_id']}`\n" - response += f" • Status: {agent['status']}\n" - if agent['version']: - response += f" • Version: `{agent['version']}`\n" - if agent['last_check']: - response += f" • Last check: {agent['last_check'][:19]}\n" - response += "\n" - - response += "**Команды управления:**\n" - response += "• `/deploy_agent ` - развернуть\n" - response += "• `/check_agents` - проверить все\n" - response += "• `/remove_server ` - удалить" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /agents: {e}") - - async def check_agents_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """CHECK AGENTS Проверить статус всех агентов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - # Отправляем начальное сообщение - status_message = await update.message.reply_text( - "🔍 *Проверка статуса агентов...*\n\n" - "⏳ Подключение к серверам...", - parse_mode='Markdown' - ) - - # Проверяем все агенты - check_results = await self.cluster_manager.check_all_agents() - - response = f"🔍 *Результаты проверки агентов*\n\n" - response += f"📊 **Статистика:**\n" - response += f" • Проверено: `{check_results['checked']}`\n" - response += f" • Онлайн: `{check_results['online']}`\n" - response += f" • Оффлайн: `{check_results['offline']}`\n" - response += f" • Ошибки: `{check_results['errors']}`\n\n" - - if check_results['details']: - response += "📋 **Детали:**\n" - for detail in check_results['details'][:10]: # Первые 10 - if 'error' in detail: - response += f"❌ `{detail['agent_id']}`: {detail['error'][:50]}...\n" - else: - status_emoji = "🟢" if detail.get('status') == 'online' else "🔴" - response += f"{status_emoji} `{detail.get('hostname', detail['agent_id'])}`: {detail.get('service_status', 'unknown')}\n" - - if len(check_results['details']) > 10: - response += f"\n... и еще {len(check_results['details']) - 10} агентов\n" - - response += f"\n🕐 Время проверки: {datetime.now().strftime('%H:%M:%S')}" - - # Обновляем сообщение - await context.bot.edit_message_text( - text=response, - chat_id=status_message.chat_id, - message_id=status_message.message_id, - parse_mode='Markdown' - ) - - # Логируем результат - logger.info(f"Проверка агентов: {check_results['online']}/{check_results['checked']} онлайн") - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /check_agents: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125202445.py b/.history/src/bot_20251125202445.py deleted file mode 100644 index ec08b14..0000000 --- a/.history/src/bot_20251125202445.py +++ /dev/null @@ -1,1345 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None, cluster_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - self.cluster_manager = cluster_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*🏢 Управление кластером:* -• `/cluster` - Информация о кластере -• `/add_server [user] [port]` - Добавить сервер -• `/remove_server [cleanup]` - Удалить сервер -• `/deploy_agent [force]` - Развернуть агент -• `/agents` - Список всех агентов -• `/check_agents` - Проверить статус агентов - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• 🏢 Централизованное управление кластером -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Новые команды управления кластером - self.application.add_handler(CommandHandler("cluster", self.cluster_command)) - self.application.add_handler(CommandHandler("add_server", self.add_server_command)) - self.application.add_handler(CommandHandler("remove_server", self.remove_server_command)) - self.application.add_handler(CommandHandler("deploy_agent", self.deploy_agent_command)) - self.application.add_handler(CommandHandler("agents", self.agents_command)) - self.application.add_handler(CommandHandler("check_agents", self.check_agents_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - # === CLUSTER MANAGEMENT COMMANDS === - - async def cluster_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """CLUSTER info Показать информацию о кластере""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - # Получаем статистику кластера - cluster_stats = await self.cluster_manager.get_cluster_stats() - agents_list = await self.cluster_manager.list_agents() - - response = f"🏢 *Кластер {cluster_stats['cluster_name']}*\n\n" - response += f"📊 **Статистика:**\n" - response += f" • Всего агентов: `{cluster_stats['total_agents']}`\n" - response += f" • Онлайн: `{cluster_stats['online_agents']}`\n" - response += f" • Оффлайн: `{cluster_stats['offline_agents']}`\n" - response += f" • Развернуто: `{cluster_stats['deployed_agents']}`\n\n" - - if agents_list: - response += "🖥️ **Агенты:**\n" - for agent in agents_list[:5]: # Показываем первые 5 - status_emoji = "🟢" if agent['status'] == 'online' else "🔴" if agent['status'] == 'offline' else "🟡" - response += f"{status_emoji} `{agent['hostname']}` ({agent['ip_address']})\n" - - if len(agents_list) > 5: - response += f"\n... и еще {len(agents_list) - 5} агентов\n" - else: - response += "📝 Агенты не добавлены\n" - - response += f"\n🕐 Последнее обновление: `{cluster_stats['last_updated'][:19]}`" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /cluster: {e}") - - async def add_server_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """ADD SERVER Добавить новый сервер в кластер""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/add_server [ssh_user] [ssh_port]`\n\n" - "Примеры:\n" - "`/add_server web-01 192.168.1.100`\n" - "`/add_server db-server 10.0.0.50 ubuntu 2222`", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - hostname = context.args[0] - ip_address = context.args[1] - ssh_user = context.args[2] if len(context.args) > 2 else 'root' - ssh_port = int(context.args[3]) if len(context.args) > 3 else 22 - - # Добавляем сервер - success, message = await self.cluster_manager.add_agent( - hostname=hostname, - ip_address=ip_address, - ssh_user=ssh_user, - ssh_port=ssh_port - ) - - if success: - response = f"✅ *Сервер добавлен в кластер*\n\n" - response += f"🖥️ **Hostname:** `{hostname}`\n" - response += f"🌐 **IP:** `{ip_address}`\n" - response += f"👤 **SSH User:** `{ssh_user}`\n" - response += f"🔌 **SSH Port:** `{ssh_port}`\n\n" - response += f"ℹ️ {message}\n\n" - response += "**Следующие шаги:**\n" - response += f"1. `/deploy_agent {self.cluster_manager.generate_agent_id(hostname, ip_address)}` - развернуть агент\n" - response += f"2. `/check_agents` - проверить статус" - - # Логируем действие - logger.info(f"Сервер {hostname} ({ip_address}) добавлен в кластер через Telegram") - else: - response = f"❌ *Не удалось добавить сервер*\n\n" - response += f"🖥️ **Hostname:** `{hostname}`\n" - response += f"🌐 **IP:** `{ip_address}`\n\n" - response += f"**Ошибка:** {message}" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /add_server: {e}") - - async def remove_server_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """REMOVE SERVER Удалить сервер из кластера""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/remove_server [cleanup]`\n\n" - "Примеры:\n" - "`/remove_server web-01-192-168-1-100`\n" - "`/remove_server web-01-192-168-1-100 cleanup` - с очисткой на сервере", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agent_id = context.args[0] - cleanup_remote = len(context.args) > 1 and context.args[1].lower() == 'cleanup' - - # Удаляем агент - success, message = await self.cluster_manager.remove_agent(agent_id, cleanup_remote) - - if success: - response = f"✅ *Сервер удален из кластера*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"🗑️ **Очистка на сервере:** {'Да' if cleanup_remote else 'Нет'}\n\n" - response += f"ℹ️ {message}" - - # Логируем действие - logger.info(f"Агент {agent_id} удален из кластера через Telegram") - else: - response = f"❌ *Не удалось удалить сервер*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n\n" - response += f"**Ошибка:** {message}" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /remove_server: {e}") - - async def deploy_agent_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """DEPLOY AGENT Развернуть PyGuardian агент на сервере""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/deploy_agent [force]`\n\n" - "Примеры:\n" - "`/deploy_agent web-01-192-168-1-100`\n" - "`/deploy_agent web-01-192-168-1-100 force` - принудительная переустановка", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agent_id = context.args[0] - force_reinstall = len(context.args) > 1 and context.args[1].lower() == 'force' - - # Отправляем начальное сообщение - status_message = await update.message.reply_text( - f"🚀 *Начинаю развертывание...*\n\n" - f"🖥️ **Agent ID:** `{agent_id}`\n" - f"⚙️ **Режим:** {'Переустановка' if force_reinstall else 'Установка'}\n\n" - f"⏳ Подключение к серверу...", - parse_mode='Markdown' - ) - - # Развертываем агент - success, message = await self.cluster_manager.deploy_agent(agent_id, force_reinstall) - - if success: - response = f"✅ *Развертывание завершено*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"🎉 **Результат:** Успешно\n\n" - response += f"ℹ️ {message}\n\n" - response += "**Следующие шаги:**\n" - response += f"• `/check_agents` - проверить статус\n" - response += f"• `/cluster` - просмотр кластера" - - # Логируем действие - logger.info(f"PyGuardian развернут на агенте {agent_id} через Telegram") - else: - response = f"❌ *Развертывание не удалось*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"💥 **Ошибка:** {message}\n\n" - response += "**Возможные решения:**\n" - response += "• Проверьте SSH соединение\n" - response += "• Убедитесь в наличии прав root\n" - response += "• Проверьте интернет соединение на сервере" - - # Обновляем сообщение - await context.bot.edit_message_text( - text=response, - chat_id=status_message.chat_id, - message_id=status_message.message_id, - parse_mode='Markdown' - ) - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /deploy_agent: {e}") - - async def agents_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """AGENTS list Показать список всех агентов кластера""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agents_list = await self.cluster_manager.list_agents() - - if not agents_list: - await update.message.reply_text( - "📝 *Агенты кластера*\n\n" - "Список пуст. Добавьте серверы командой `/add_server`", - parse_mode='Markdown' - ) - return - - response = f"🖥️ *Агенты кластера* ({len(agents_list)})\n\n" - - for agent in agents_list: - status_emoji = { - 'online': '🟢', - 'offline': '🔴', - 'deployed': '🟡', - 'added': '⚪' - }.get(agent['status'], '❓') - - response += f"{status_emoji} **{agent['hostname']}**\n" - response += f" • IP: `{agent['ip_address']}`\n" - response += f" • ID: `{agent['agent_id']}`\n" - response += f" • Status: {agent['status']}\n" - if agent['version']: - response += f" • Version: `{agent['version']}`\n" - if agent['last_check']: - response += f" • Last check: {agent['last_check'][:19]}\n" - response += "\n" - - response += "**Команды управления:**\n" - response += "• `/deploy_agent ` - развернуть\n" - response += "• `/check_agents` - проверить все\n" - response += "• `/remove_server ` - удалить" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /agents: {e}") - - async def check_agents_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """CHECK AGENTS Проверить статус всех агентов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - # Отправляем начальное сообщение - status_message = await update.message.reply_text( - "🔍 *Проверка статуса агентов...*\n\n" - "⏳ Подключение к серверам...", - parse_mode='Markdown' - ) - - # Проверяем все агенты - check_results = await self.cluster_manager.check_all_agents() - - response = f"🔍 *Результаты проверки агентов*\n\n" - response += f"📊 **Статистика:**\n" - response += f" • Проверено: `{check_results['checked']}`\n" - response += f" • Онлайн: `{check_results['online']}`\n" - response += f" • Оффлайн: `{check_results['offline']}`\n" - response += f" • Ошибки: `{check_results['errors']}`\n\n" - - if check_results['details']: - response += "📋 **Детали:**\n" - for detail in check_results['details'][:10]: # Первые 10 - if 'error' in detail: - response += f"❌ `{detail['agent_id']}`: {detail['error'][:50]}...\n" - else: - status_emoji = "🟢" if detail.get('status') == 'online' else "🔴" - response += f"{status_emoji} `{detail.get('hostname', detail['agent_id'])}`: {detail.get('service_status', 'unknown')}\n" - - if len(check_results['details']) > 10: - response += f"\n... и еще {len(check_results['details']) - 10} агентов\n" - - response += f"\n🕐 Время проверки: {datetime.now().strftime('%H:%M:%S')}" - - # Обновляем сообщение - await context.bot.edit_message_text( - text=response, - chat_id=status_message.chat_id, - message_id=status_message.message_id, - parse_mode='Markdown' - ) - - # Логируем результат - logger.info(f"Проверка агентов: {check_results['online']}/{check_results['checked']} онлайн") - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /check_agents: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/bot_20251125203709.py b/.history/src/bot_20251125203709.py deleted file mode 100644 index ec08b14..0000000 --- a/.history/src/bot_20251125203709.py +++ /dev/null @@ -1,1345 +0,0 @@ -""" -Bot module для PyGuardian -Telegram бот для управления системой защиты -""" - -import asyncio -import logging -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup -from telegram.ext import Application, CommandHandler, CallbackQueryHandler, ContextTypes -from telegram.constants import ParseMode -import ipaddress - -logger = logging.getLogger(__name__) - - -class TelegramBot: - """Telegram бот для управления PyGuardian""" - - def __init__(self, config: Dict, storage, firewall_manager, attack_detector, - security_manager=None, session_manager=None, password_manager=None, cluster_manager=None): - self.token = config.get('bot_token') - self.admin_id = config.get('admin_id') - self.storage = storage - self.firewall_manager = firewall_manager - self.attack_detector = attack_detector - self.security_manager = security_manager - self.session_manager = session_manager - self.password_manager = password_manager - self.cluster_manager = cluster_manager - - if not self.token or not self.admin_id: - raise ValueError("Не указан токен бота или admin_id в конфигурации") - - self.application = None - self.running = False - - def _check_admin(self, user_id: int) -> bool: - """Проверка прав администратора""" - return user_id == self.admin_id - - async def _send_unauthorized_message(self, update: Update) -> None: - """Отправка сообщения о недостатке прав""" - await update.message.reply_text( - "❌ *Нет доступа*\n\nВы не авторизованы для использования этого бота.", - parse_mode=ParseMode.MARKDOWN - ) - - async def start_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /start""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - welcome_message = """ -🛡️ *PyGuardian Protection System* - -Добро пожаловать! Система активна и готова к работе. - -*Доступные команды:* -• `/status` - Текущая статистика -• `/top10` - Топ атакующих IP -• `/details ` - Информация по IP -• `/ban ` - Ручная блокировка -• `/unban ` - Разблокировать IP -• `/list` - Список забаненных IP -• `/help` - Справка - -*Системная информация:* -• Мониторинг: auth.log -• Firewall: {backend} -• База данных: Активна - """.format(backend=self.firewall_manager.backend.upper()) - - await update.message.reply_text(welcome_message, parse_mode=ParseMode.MARKDOWN) - - async def status_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /status""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - # Получаем статистику - stats = await self.storage.get_daily_stats() - top_attackers = await self.storage.get_top_attackers(limit=5) - banned_ips = await self.storage.get_banned_ips() - - # Формируем сообщение - message = f""" -📊 *Статистика за сегодня* - -🚨 *Атаки:* {stats['today']['attacks']} - {'+' if stats['attack_change'] >= 0 else ''}{stats['attack_change']} по сравнению с вчера - -🌐 *Уникальных IP:* {stats['today']['unique_ips']} - -🔒 *Активных банов:* {stats['active_bans']} - -✅ *Успешных входов:* {stats['today']['successful_logins']} - -🔝 *Топ атакующих:* -""" - - for i, attacker in enumerate(top_attackers, 1): - message += f"{i}. `{attacker['ip']}` - {attacker['attempts']} попыток\n" - - if not top_attackers: - message += "Сегодня атак не обнаружено 🎉\n" - - # Добавляем клавиатуру - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_status")], - [InlineKeyboardButton("📋 Топ-10", callback_data="show_top10"), - InlineKeyboardButton("🚫 Список банов", callback_data="show_banned")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде status: {e}") - await update.message.reply_text( - f"❌ Ошибка получения статистики: {str(e)}" - ) - - async def top10_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /top10""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - top_attackers = await self.storage.get_top_attackers(limit=10, days=1) - - if not top_attackers: - await update.message.reply_text( - "📈 *Топ-10 атакующих*\n\nСегодня атак не обнаружено 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = "📈 *Топ-10 атакующих IP за сутки*\n\n" - - for i, attacker in enumerate(top_attackers, 1): - emoji = "🥇" if i == 1 else "🥈" if i == 2 else "🥉" if i == 3 else "🔸" - message += f"{emoji} `{attacker['ip']}`\n" - message += f" 📊 {attacker['attempts']} попыток\n" - message += f" 🕐 {attacker['first_attempt']} - {attacker['last_attempt']}\n" - if attacker['attack_types']: - message += f" 🎯 {', '.join(attacker['attack_types'][:2])}\n" - message += "\n" - - # Добавляем кнопку для получения деталей - keyboard = [[InlineKeyboardButton("🔍 Детали по IP", callback_data="show_details_menu")]] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде top10: {e}") - await update.message.reply_text( - f"❌ Ошибка получения топ атакующих: {str(e)}" - ) - - async def details_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /details """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/details `\n\nПример: `/details 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - details = await self.storage.get_ip_details(ip) - - message = f"🔍 *Детали для IP:* `{ip}`\n\n" - - if details['total_attempts'] == 0: - message += "ℹ️ Атак от данного IP не обнаружено" - else: - message += f"📊 *Общая статистика:*\n" - message += f"• Всего попыток: {details['total_attempts']}\n" - message += f"• Первая атака: {details['first_seen']}\n" - message += f"• Последняя атака: {details['last_seen']}\n" - - if details['attack_types']: - message += f"• Типы атак: {', '.join(details['attack_types'])}\n" - - if details['usernames']: - message += f"• Пользователи: {', '.join(details['usernames'][:5])}\n" - - # Статус бана - if details['is_banned']: - ban_info = details['ban_info'] - message += f"\n🚫 *Статус:* ЗАБЛОКИРОВАН\n" - message += f"• Причина: {ban_info['reason']}\n" - message += f"• Заблокирован: {ban_info['banned_at']}\n" - message += f"• Разбан: {ban_info['unban_at']}\n" - message += f"• Ручной: {'Да' if ban_info['manual'] else 'Нет'}\n" - else: - message += f"\n✅ *Статус:* НЕ ЗАБЛОКИРОВАН\n" - - # Последние попытки - if details['recent_attempts']: - message += f"\n📋 *Последние попытки:*\n" - for attempt in details['recent_attempts'][:5]: - message += f"• {attempt['timestamp']} - {attempt['type']} ({attempt['username']})\n" - - # Кнопки управления - keyboard = [] - if details['is_banned']: - keyboard.append([InlineKeyboardButton("🔓 Разбанить", callback_data=f"unban_{ip}")]) - else: - keyboard.append([InlineKeyboardButton("🚫 Забанить", callback_data=f"ban_{ip}")]) - - keyboard.append([InlineKeyboardButton("🔄 Обновить", callback_data=f"details_{ip}")]) - - if keyboard: - reply_markup = InlineKeyboardMarkup(keyboard) - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - else: - await update.message.reply_text(message, parse_mode=ParseMode.MARKDOWN) - - except Exception as e: - logger.error(f"Ошибка в команде details: {e}") - await update.message.reply_text( - f"❌ Ошибка получения деталей для IP {ip}: {str(e)}" - ) - - async def ban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /ban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/ban `\n\nПример: `/ban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно заблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось заблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде ban: {e}") - await update.message.reply_text( - f"❌ Ошибка блокировки IP {ip}: {str(e)}" - ) - - async def unban_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /unban """ - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "❓ Использование: `/unban `\n\nПример: `/unban 192.168.1.100`", - parse_mode=ParseMode.MARKDOWN - ) - return - - ip = context.args[0] - - # Валидация IP - try: - ipaddress.ip_address(ip) - except ValueError: - await update.message.reply_text( - f"❌ Некорректный IP-адрес: `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - return - - try: - success = await self.attack_detector.process_unban(ip) - - if success: - await update.message.reply_text( - f"✅ IP `{ip}` успешно разблокирован", - parse_mode=ParseMode.MARKDOWN - ) - else: - await update.message.reply_text( - f"❌ Не удалось разблокировать IP `{ip}`", - parse_mode=ParseMode.MARKDOWN - ) - - except Exception as e: - logger.error(f"Ошибка в команде unban: {e}") - await update.message.reply_text( - f"❌ Ошибка разблокировки IP {ip}: {str(e)}" - ) - - async def list_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /list""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - banned_ips = await self.storage.get_banned_ips() - - if not banned_ips: - await update.message.reply_text( - "📋 *Список забаненных IP*\n\nСписок пуст 🎉", - parse_mode=ParseMode.MARKDOWN - ) - return - - message = f"📋 *Забаненные IP ({len(banned_ips)})*\n\n" - - for i, ban in enumerate(banned_ips[:15], 1): # Показываем первые 15 - emoji = "🔴" if ban['manual'] else "🟡" - message += f"{emoji} `{ban['ip']}`\n" - message += f" 📝 {ban['reason'][:50]}...\n" if len(ban['reason']) > 50 else f" 📝 {ban['reason']}\n" - message += f" 🕐 {ban['banned_at']}\n" - if ban['attempts'] > 0: - message += f" 📊 {ban['attempts']} попыток\n" - message += "\n" - - if len(banned_ips) > 15: - message += f"\n... и еще {len(banned_ips) - 15} IP" - - # Добавляем кнопки управления - keyboard = [ - [InlineKeyboardButton("🔄 Обновить", callback_data="refresh_banned")], - [InlineKeyboardButton("🧹 Очистить истекшие", callback_data="cleanup_expired")] - ] - reply_markup = InlineKeyboardMarkup(keyboard) - - await update.message.reply_text( - message, - parse_mode=ParseMode.MARKDOWN, - reply_markup=reply_markup - ) - - except Exception as e: - logger.error(f"Ошибка в команде list: {e}") - await update.message.reply_text( - f"❌ Ошибка получения списка забаненных IP: {str(e)}" - ) - - async def help_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Команда /help""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - help_text = """ -🛡️ *PyGuardian - Справка* - -*Основные команды:* -• `/start` - Запуск и приветствие -• `/status` - Текущая статистика системы -• `/top10` - Топ-10 атакующих IP за сутки -• `/details ` - Подробная информация по IP -• `/ban ` - Ручная блокировка IP -• `/unban ` - Разблокировка IP -• `/list` - Список всех забаненных IP - -*🚨 Управление компромиссами:* -• `/compromises` - Список обнаруженных взломов -• `/sessions` - Активные SSH сессии -• `/kick ` - Завершить сессию пользователя -• `/generate_password ` - Сгенерировать пароль -• `/set_password ` - Установить пароль - -*🏢 Управление кластером:* -• `/cluster` - Информация о кластере -• `/add_server [user] [port]` - Добавить сервер -• `/remove_server [cleanup]` - Удалить сервер -• `/deploy_agent [force]` - Развернуть агент -• `/agents` - Список всех агентов -• `/check_agents` - Проверить статус агентов - -*ℹ️ Информация:* -• `/help` - Эта справка - -*Системная информация:* -• Мониторинг auth.log в реальном времени -• Автоматическая блокировка при превышении лимита -• Поддержка iptables и nftables -• Автоматический разбан по таймеру -• 🚨 СКРЫТНАЯ реакция на взломы -• 🏢 Централизованное управление кластером -• Уведомления о критических событиях - -*Безопасность:* -• Доступ только для авторизованных пользователей -• Белый список IP для исключений -• Защита от ложных срабатываний -• Логирование всех действий - -*Поддержка:* @your_support_bot - """ - - await update.message.reply_text(help_text, parse_mode=ParseMode.MARKDOWN) - - async def button_callback(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """Обработчик кнопок""" - query = update.callback_query - await query.answer() - - if not self._check_admin(query.from_user.id): - await query.message.reply_text("❌ Нет доступа") - return - - data = query.data - - try: - if data == "refresh_status": - # Обновляем статус - await self.status_command(query, context) - - elif data == "show_top10": - # Показываем топ-10 - await self.top10_command(query, context) - - elif data == "show_banned": - # Показываем список банов - await self.list_command(query, context) - - elif data == "refresh_banned": - # Обновляем список банов - await self.list_command(query, context) - - elif data == "cleanup_expired": - # Очищаем истекшие баны - await self.attack_detector.check_expired_bans() - await query.message.reply_text("✅ Очистка истекших банов выполнена") - - elif data.startswith("ban_"): - ip = data[4:] - success = await self.attack_detector.manual_ban(ip, "Ручная блокировка через Telegram бот") - if success: - await query.message.reply_text(f"✅ IP `{ip}` заблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка блокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("unban_"): - ip = data[6:] - success = await self.attack_detector.process_unban(ip) - if success: - await query.message.reply_text(f"✅ IP `{ip}` разблокирован", parse_mode=ParseMode.MARKDOWN) - else: - await query.message.reply_text(f"❌ Ошибка разблокировки IP `{ip}`", parse_mode=ParseMode.MARKDOWN) - - elif data.startswith("details_"): - ip = data[8:] - # Отправляем детали как новое сообщение - context.args = [ip] - await self.details_command(query, context) - - except Exception as e: - logger.error(f"Ошибка в обработчике кнопок: {e}") - await query.message.reply_text(f"❌ Ошибка: {str(e)}") - - async def send_notification(self, message: str) -> None: - """Отправка уведомления администратору""" - try: - if self.application and self.running: - await self.application.bot.send_message( - chat_id=self.admin_id, - text=message, - parse_mode=ParseMode.MARKDOWN - ) - except Exception as e: - logger.error(f"Ошибка отправки уведомления: {e}") - - async def start_bot(self) -> None: - """Запуск бота""" - try: - self.application = Application.builder().token(self.token).build() - - # Добавляем обработчики команд - self.application.add_handler(CommandHandler("start", self.start_command)) - self.application.add_handler(CommandHandler("status", self.status_command)) - self.application.add_handler(CommandHandler("top10", self.top10_command)) - self.application.add_handler(CommandHandler("details", self.details_command)) - self.application.add_handler(CommandHandler("ban", self.ban_command)) - self.application.add_handler(CommandHandler("unban", self.unban_command)) - self.application.add_handler(CommandHandler("list", self.list_command)) - self.application.add_handler(CommandHandler("help", self.help_command)) - - # Новые команды для управления компромиссами и сессиями - self.application.add_handler(CommandHandler("compromises", self.compromises_command)) - self.application.add_handler(CommandHandler("sessions", self.sessions_command)) - self.application.add_handler(CommandHandler("kick", self.kick_command)) - self.application.add_handler(CommandHandler("generate_password", self.generate_password_command)) - self.application.add_handler(CommandHandler("set_password", self.set_password_command)) - - # Новые команды управления кластером - self.application.add_handler(CommandHandler("cluster", self.cluster_command)) - self.application.add_handler(CommandHandler("add_server", self.add_server_command)) - self.application.add_handler(CommandHandler("remove_server", self.remove_server_command)) - self.application.add_handler(CommandHandler("deploy_agent", self.deploy_agent_command)) - self.application.add_handler(CommandHandler("agents", self.agents_command)) - self.application.add_handler(CommandHandler("check_agents", self.check_agents_command)) - - # Добавляем обработчик кнопок - self.application.add_handler(CallbackQueryHandler(self.button_callback)) - - self.running = True - logger.info("Telegram бот запущен") - - # Запускаем бота - await self.application.initialize() - await self.application.start() - - # Уведомляем о запуске - await self.send_notification("🟢 *PyGuardian запущен*\n\nСистема защиты активирована и готова к работе.") - - # Держим бота работающим - await self.application.updater.start_polling() - - except Exception as e: - logger.error(f"Ошибка запуска Telegram бота: {e}") - self.running = False - - async def stop_bot(self) -> None: - """Остановка бота""" - try: - if self.application and self.running: - # Уведомляем об остановке - await self.send_notification("🔴 *PyGuardian остановлен*\n\nСистема защиты деактивирована.") - - await self.application.updater.stop() - await self.application.stop() - await self.application.shutdown() - - self.running = False - logger.info("Telegram бот остановлен") - - except Exception as e: - logger.error(f"Ошибка остановки Telegram бота: {e}") - - async def compromises_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET compromises Показать список обнаруженных компромиссов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.security_manager: - await update.message.reply_text( - "⚠️ Менеджер безопасности не доступен", - parse_mode='Markdown' - ) - return - - compromises = await self.storage.get_compromises() - - if not compromises: - await update.message.reply_text( - "🎉 *Компромиссы не обнаружены*\n\nСистема работает нормально.", - parse_mode='Markdown' - ) - return - - response = "🚨 *Обнаруженные компромиссы*\n\n" - - for comp in compromises[:10]: # Показываем только 10 последних - timestamp = comp['timestamp'] - ip = comp['ip_address'] - user = comp['username'] - action = comp['action_taken'] - evidence = comp['evidence_data'][:100] if comp['evidence_data'] else "Нет" - - response += f"🚨 `{timestamp}`\n" - response += f" • IP: `{ip}`\n" - response += f" • User: `{user}`\n" - response += f" • Действие: {action}\n" - response += f" • Доказательства: {evidence}...\n\n" - - if len(compromises) > 10: - response += f"ℹ️ *Показано 10 из {len(compromises)} компромиссов*" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /compromises: {e}") - - async def sessions_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GET sessions Показать активные SSH сессии""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - sessions = self.session_manager.get_active_sessions() - - if not sessions: - await update.message.reply_text( - "🎉 *Активные SSH сессии отсутствуют*", - parse_mode='Markdown' - ) - return - - response = f"💻 *Активные SSH сессии* ({len(sessions)})\n\n" - - for session in sessions: - pid = session.get('pid', 'Неизвестно') - user = session.get('username', 'Неизвестно') - tty = session.get('tty', 'Неизвестно') - start_time = session.get('start_time', 'Неизвестно') - remote_ip = session.get('remote_ip', 'Неизвестно') - - response += f"💻 PID: `{pid}`\n" - response += f" • User: `{user}`\n" - response += f" • TTY: `{tty}`\n" - response += f" • IP: `{remote_ip}`\n" - response += f" • Start: `{start_time}`\n\n" - - response += "ℹ️ Для завершения сессии используйте:\n`/kick ` или `/kick `" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /sessions: {e}") - - async def kick_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """KICK user/pid Завершить сессию пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/kick `\n`/kick `", - parse_mode='Markdown' - ) - return - - try: - if not self.session_manager: - await update.message.reply_text( - "⚠️ Менеджер сессий не доступен", - parse_mode='Markdown' - ) - return - - target = context.args[0] - - # Проверяем, является ли target PID (число) - if target.isdigit(): - result = self.session_manager.terminate_session(pid=int(target)) - action = f"PID {target}" - else: - result = self.session_manager.terminate_session(username=target) - action = f"user {target}" - - if result: - response = f"✅ *Сессия завершена*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия была успешно завершена" - - # Логируем действие - logger.info(f"Сессия завершена через Telegram бот: {action}") - else: - response = f"❌ *Не удалось завершить сессию*\n\n" - response += f"💻 Target: `{action}`\n" - response += f"ℹ️ Сессия может быть уже завершена или не существовать" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /kick: {e}") - - async def generate_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """GENERATE PASSWORD Сгенерировать сложный пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/generate_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - new_password = self.password_manager.generate_password() - success = self.password_manager.set_user_password(username, new_password) - - if success: - response = f"✅ *Пароль успешно сгенерирован*\n\n" - response += f"💻 User: `{username}`\n" - response += f"🔐 Password: `{new_password}`\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Сохраните пароль в надёжном месте\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль сгенерирован через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /generate_password: {e}") - - async def set_password_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """SET PASSWORD Установить пароль для пользователя""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\nИспользование:\n`/set_password `", - parse_mode='Markdown' - ) - return - - try: - if not self.password_manager: - await update.message.reply_text( - "⚠️ Менеджер паролей не доступен", - parse_mode='Markdown' - ) - return - - username = context.args[0] - password = context.args[1] - - # Проверяем, существует ли пользователь - user_exists = self.password_manager.user_exists(username) - if not user_exists: - await update.message.reply_text( - f"❌ *Пользователь не найден*\n\nПользователь `{username}` не существует в системе.", - parse_mode='Markdown' - ) - return - - # Проверяем сложность пароля - if len(password) < 8: - await update.message.reply_text( - "❌ *Пароль слишком простой*\n\nПароль должен содержать минимум 8 символов.", - parse_mode='Markdown' - ) - return - - success = self.password_manager.set_user_password(username, password) - - if success: - response = f"✅ *Пароль успешно установлен*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Пароль изменён успешно\n\n" - response += "⚠️ *ВНИМАНИЕ:*\n" - response += "• Это сообщение будет удалено через 5 минут" - - # Логируем действие (без пароля!) - logger.info(f"Пароль установлен через Telegram бот для пользователя: {username}") - - # Отправляем сообщение и планируем его удаление - message = await update.message.reply_text(response, parse_mode='Markdown') - - # Планируем удаление через 5 минут (безопасность) - context.job_queue.run_once( - self._delete_message, - 300, # 5 минут - data={'chat_id': message.chat_id, 'message_id': message.message_id} - ) - - else: - response = f"❌ *Не удалось установить пароль*\n\n" - response += f"💻 User: `{username}`\n" - response += f"ℹ️ Проверьте права доступа и существование пользователя" - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /set_password: {e}") - - async def _delete_message(self, context): - """Helper метод для удаления сообщения (безопасность)""" - try: - data = context.job.data - await context.bot.delete_message( - chat_id=data['chat_id'], - message_id=data['message_id'] - ) - except Exception as e: - logger.warning(f"Не удалось удалить сообщение: {e}") - - # === CLUSTER MANAGEMENT COMMANDS === - - async def cluster_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """CLUSTER info Показать информацию о кластере""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - # Получаем статистику кластера - cluster_stats = await self.cluster_manager.get_cluster_stats() - agents_list = await self.cluster_manager.list_agents() - - response = f"🏢 *Кластер {cluster_stats['cluster_name']}*\n\n" - response += f"📊 **Статистика:**\n" - response += f" • Всего агентов: `{cluster_stats['total_agents']}`\n" - response += f" • Онлайн: `{cluster_stats['online_agents']}`\n" - response += f" • Оффлайн: `{cluster_stats['offline_agents']}`\n" - response += f" • Развернуто: `{cluster_stats['deployed_agents']}`\n\n" - - if agents_list: - response += "🖥️ **Агенты:**\n" - for agent in agents_list[:5]: # Показываем первые 5 - status_emoji = "🟢" if agent['status'] == 'online' else "🔴" if agent['status'] == 'offline' else "🟡" - response += f"{status_emoji} `{agent['hostname']}` ({agent['ip_address']})\n" - - if len(agents_list) > 5: - response += f"\n... и еще {len(agents_list) - 5} агентов\n" - else: - response += "📝 Агенты не добавлены\n" - - response += f"\n🕐 Последнее обновление: `{cluster_stats['last_updated'][:19]}`" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /cluster: {e}") - - async def add_server_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """ADD SERVER Добавить новый сервер в кластер""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if len(context.args) < 2: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/add_server [ssh_user] [ssh_port]`\n\n" - "Примеры:\n" - "`/add_server web-01 192.168.1.100`\n" - "`/add_server db-server 10.0.0.50 ubuntu 2222`", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - hostname = context.args[0] - ip_address = context.args[1] - ssh_user = context.args[2] if len(context.args) > 2 else 'root' - ssh_port = int(context.args[3]) if len(context.args) > 3 else 22 - - # Добавляем сервер - success, message = await self.cluster_manager.add_agent( - hostname=hostname, - ip_address=ip_address, - ssh_user=ssh_user, - ssh_port=ssh_port - ) - - if success: - response = f"✅ *Сервер добавлен в кластер*\n\n" - response += f"🖥️ **Hostname:** `{hostname}`\n" - response += f"🌐 **IP:** `{ip_address}`\n" - response += f"👤 **SSH User:** `{ssh_user}`\n" - response += f"🔌 **SSH Port:** `{ssh_port}`\n\n" - response += f"ℹ️ {message}\n\n" - response += "**Следующие шаги:**\n" - response += f"1. `/deploy_agent {self.cluster_manager.generate_agent_id(hostname, ip_address)}` - развернуть агент\n" - response += f"2. `/check_agents` - проверить статус" - - # Логируем действие - logger.info(f"Сервер {hostname} ({ip_address}) добавлен в кластер через Telegram") - else: - response = f"❌ *Не удалось добавить сервер*\n\n" - response += f"🖥️ **Hostname:** `{hostname}`\n" - response += f"🌐 **IP:** `{ip_address}`\n\n" - response += f"**Ошибка:** {message}" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /add_server: {e}") - - async def remove_server_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """REMOVE SERVER Удалить сервер из кластера""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/remove_server [cleanup]`\n\n" - "Примеры:\n" - "`/remove_server web-01-192-168-1-100`\n" - "`/remove_server web-01-192-168-1-100 cleanup` - с очисткой на сервере", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agent_id = context.args[0] - cleanup_remote = len(context.args) > 1 and context.args[1].lower() == 'cleanup' - - # Удаляем агент - success, message = await self.cluster_manager.remove_agent(agent_id, cleanup_remote) - - if success: - response = f"✅ *Сервер удален из кластера*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"🗑️ **Очистка на сервере:** {'Да' if cleanup_remote else 'Нет'}\n\n" - response += f"ℹ️ {message}" - - # Логируем действие - logger.info(f"Агент {agent_id} удален из кластера через Telegram") - else: - response = f"❌ *Не удалось удалить сервер*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n\n" - response += f"**Ошибка:** {message}" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /remove_server: {e}") - - async def deploy_agent_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """DEPLOY AGENT Развернуть PyGuardian агент на сервере""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - if not context.args: - await update.message.reply_text( - "⚠️ *Неверный формат*\n\n" - "Использование:\n" - "`/deploy_agent [force]`\n\n" - "Примеры:\n" - "`/deploy_agent web-01-192-168-1-100`\n" - "`/deploy_agent web-01-192-168-1-100 force` - принудительная переустановка", - parse_mode='Markdown' - ) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agent_id = context.args[0] - force_reinstall = len(context.args) > 1 and context.args[1].lower() == 'force' - - # Отправляем начальное сообщение - status_message = await update.message.reply_text( - f"🚀 *Начинаю развертывание...*\n\n" - f"🖥️ **Agent ID:** `{agent_id}`\n" - f"⚙️ **Режим:** {'Переустановка' if force_reinstall else 'Установка'}\n\n" - f"⏳ Подключение к серверу...", - parse_mode='Markdown' - ) - - # Развертываем агент - success, message = await self.cluster_manager.deploy_agent(agent_id, force_reinstall) - - if success: - response = f"✅ *Развертывание завершено*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"🎉 **Результат:** Успешно\n\n" - response += f"ℹ️ {message}\n\n" - response += "**Следующие шаги:**\n" - response += f"• `/check_agents` - проверить статус\n" - response += f"• `/cluster` - просмотр кластера" - - # Логируем действие - logger.info(f"PyGuardian развернут на агенте {agent_id} через Telegram") - else: - response = f"❌ *Развертывание не удалось*\n\n" - response += f"🖥️ **Agent ID:** `{agent_id}`\n" - response += f"💥 **Ошибка:** {message}\n\n" - response += "**Возможные решения:**\n" - response += "• Проверьте SSH соединение\n" - response += "• Убедитесь в наличии прав root\n" - response += "• Проверьте интернет соединение на сервере" - - # Обновляем сообщение - await context.bot.edit_message_text( - text=response, - chat_id=status_message.chat_id, - message_id=status_message.message_id, - parse_mode='Markdown' - ) - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /deploy_agent: {e}") - - async def agents_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """AGENTS list Показать список всех агентов кластера""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - agents_list = await self.cluster_manager.list_agents() - - if not agents_list: - await update.message.reply_text( - "📝 *Агенты кластера*\n\n" - "Список пуст. Добавьте серверы командой `/add_server`", - parse_mode='Markdown' - ) - return - - response = f"🖥️ *Агенты кластера* ({len(agents_list)})\n\n" - - for agent in agents_list: - status_emoji = { - 'online': '🟢', - 'offline': '🔴', - 'deployed': '🟡', - 'added': '⚪' - }.get(agent['status'], '❓') - - response += f"{status_emoji} **{agent['hostname']}**\n" - response += f" • IP: `{agent['ip_address']}`\n" - response += f" • ID: `{agent['agent_id']}`\n" - response += f" • Status: {agent['status']}\n" - if agent['version']: - response += f" • Version: `{agent['version']}`\n" - if agent['last_check']: - response += f" • Last check: {agent['last_check'][:19]}\n" - response += "\n" - - response += "**Команды управления:**\n" - response += "• `/deploy_agent ` - развернуть\n" - response += "• `/check_agents` - проверить все\n" - response += "• `/remove_server ` - удалить" - - await update.message.reply_text(response, parse_mode='Markdown') - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /agents: {e}") - - async def check_agents_command(self, update: Update, context: ContextTypes.DEFAULT_TYPE) -> None: - """CHECK AGENTS Проверить статус всех агентов""" - if not self._check_admin(update.effective_user.id): - await self._send_unauthorized_message(update) - return - - try: - if not self.cluster_manager: - await update.message.reply_text( - "⚠️ Менеджер кластера не доступен", - parse_mode='Markdown' - ) - return - - # Отправляем начальное сообщение - status_message = await update.message.reply_text( - "🔍 *Проверка статуса агентов...*\n\n" - "⏳ Подключение к серверам...", - parse_mode='Markdown' - ) - - # Проверяем все агенты - check_results = await self.cluster_manager.check_all_agents() - - response = f"🔍 *Результаты проверки агентов*\n\n" - response += f"📊 **Статистика:**\n" - response += f" • Проверено: `{check_results['checked']}`\n" - response += f" • Онлайн: `{check_results['online']}`\n" - response += f" • Оффлайн: `{check_results['offline']}`\n" - response += f" • Ошибки: `{check_results['errors']}`\n\n" - - if check_results['details']: - response += "📋 **Детали:**\n" - for detail in check_results['details'][:10]: # Первые 10 - if 'error' in detail: - response += f"❌ `{detail['agent_id']}`: {detail['error'][:50]}...\n" - else: - status_emoji = "🟢" if detail.get('status') == 'online' else "🔴" - response += f"{status_emoji} `{detail.get('hostname', detail['agent_id'])}`: {detail.get('service_status', 'unknown')}\n" - - if len(check_results['details']) > 10: - response += f"\n... и еще {len(check_results['details']) - 10} агентов\n" - - response += f"\n🕐 Время проверки: {datetime.now().strftime('%H:%M:%S')}" - - # Обновляем сообщение - await context.bot.edit_message_text( - text=response, - chat_id=status_message.chat_id, - message_id=status_message.message_id, - parse_mode='Markdown' - ) - - # Логируем результат - logger.info(f"Проверка агентов: {check_results['online']}/{check_results['checked']} онлайн") - - except Exception as e: - await update.message.reply_text( - f"❌ Ошибка: {e}", - parse_mode='Markdown' - ) - logger.error(f"Ошибка при выполнении команды /check_agents: {e}") - - -class NotificationManager: - """Менеджер уведомлений""" - - def __init__(self, bot: TelegramBot): - self.bot = bot - - async def on_ip_banned(self, ban_info: Dict) -> None: - """Уведомление о блокировке IP""" - try: - emoji = "🔴" if not ban_info['auto'] else "🟡" - ban_type = "Ручная блокировка" if not ban_info['auto'] else "Автоматическая блокировка" - - message = f"""{emoji} *{ban_type}* - -🚫 IP: `{ban_info['ip']}` -📝 Причина: {ban_info['reason']} -""" - - if ban_info['attempts'] > 0: - message += f"📊 Попыток: {ban_info['attempts']}\n" - - message += f"⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о бане: {e}") - - async def on_ip_unbanned(self, unban_info: Dict) -> None: - """Уведомление о разблокировке IP""" - try: - emoji = "🟢" if unban_info['auto'] else "🔓" - unban_type = "Автоматическая разблокировка" if unban_info['auto'] else "Ручная разблокировка" - - message = f"""{emoji} *{unban_type}* - -✅ IP: `{unban_info['ip']}` -⏰ Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о разбане: {e}") - - async def on_critical_attack(self, ip: str, attempts: int) -> None: - """Уведомление о критической атаке""" - try: - message = f"""⚠️ *КРИТИЧЕСКАЯ АТАКА* - -🚨 IP: `{ip}` -📊 Попыток: {attempts} за последние 5 минут -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Рекомендуется ручная проверка! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о критической атаке: {e}") - - async def on_system_error(self, error: str) -> None: - """Уведомление о системной ошибке""" - try: - message = f"""❌ *СИСТЕМНАЯ ОШИБКА* - -🔧 Описание: {error} -🕐 Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - -Требует внимания администратора! -""" - - await self.bot.send_notification(message) - - except Exception as e: - logger.error(f"Ошибка отправки уведомления о системной ошибке: {e}") \ No newline at end of file diff --git a/.history/src/cluster_manager_20251125202224.py b/.history/src/cluster_manager_20251125202224.py deleted file mode 100644 index e05dac3..0000000 --- a/.history/src/cluster_manager_20251125202224.py +++ /dev/null @@ -1,622 +0,0 @@ -""" -Cluster Manager для PyGuardian -Управление кластером серверов и автоматическое развертывание агентов -""" - -import asyncio -import logging -import json -import subprocess -import os -import yaml -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from pathlib import Path -import aiofiles -import paramiko -from cryptography.fernet import Fernet -import secrets -import string - -logger = logging.getLogger(__name__) - - -class ServerAgent: - """Представление удаленного сервера-агента""" - - def __init__(self, server_id: str, config: Dict): - self.server_id = server_id - self.hostname = config.get('hostname') - self.ip_address = config.get('ip_address') - self.ssh_port = config.get('ssh_port', 22) - self.ssh_user = config.get('ssh_user', 'root') - self.ssh_key_path = config.get('ssh_key_path') - self.ssh_password = config.get('ssh_password') - self.status = 'unknown' - self.last_check = None - self.version = None - self.stats = {} - - def to_dict(self) -> Dict: - """Конвертация в словарь для сериализации""" - return { - 'server_id': self.server_id, - 'hostname': self.hostname, - 'ip_address': self.ip_address, - 'ssh_port': self.ssh_port, - 'ssh_user': self.ssh_user, - 'ssh_key_path': self.ssh_key_path, - 'status': self.status, - 'last_check': self.last_check.isoformat() if self.last_check else None, - 'version': self.version, - 'stats': self.stats - } - - -class ClusterManager: - """Менеджер кластера серверов""" - - def __init__(self, storage, config: Dict): - self.storage = storage - self.config = config - - # Параметры кластера - self.cluster_name = config.get('cluster_name', 'PyGuardian-Cluster') - self.master_server = config.get('master_server', True) - self.agents_config_path = config.get('agents_config_path', '/var/lib/pyguardian/agents.yaml') - self.deployment_path = config.get('deployment_path', '/opt/pyguardian') - - # SSH настройки - self.ssh_timeout = config.get('ssh_timeout', 30) - self.ssh_retries = config.get('ssh_retries', 3) - - # Шифрование - self.encryption_key = self._get_or_create_cluster_key() - self.cipher = Fernet(self.encryption_key) - - # Кэш агентов - self.agents: Dict[str, ServerAgent] = {} - - # Шаблоны для развертывания - self.deployment_script = self._get_deployment_script() - - def _get_or_create_cluster_key(self) -> bytes: - """Получить или создать ключ шифрования кластера""" - key_file = "/var/lib/pyguardian/cluster_encryption.key" - try: - os.makedirs(os.path.dirname(key_file), exist_ok=True) - - if os.path.exists(key_file): - with open(key_file, 'rb') as f: - return f.read() - else: - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) - logger.info("Создан новый ключ шифрования кластера") - return key - except Exception as e: - logger.error(f"Ошибка работы с ключом кластера: {e}") - return Fernet.generate_key() - - def _get_deployment_script(self) -> str: - """Получить скрипт развертывания агента""" - return '''#!/bin/bash -# PyGuardian Agent Deployment Script -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_NAME="pyguardian-agent" -GITHUB_REPO="https://github.com/your-repo/PyGuardian.git" - -echo "🛡️ Начинаю установку PyGuardian Agent..." - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Скрипт должен быть запущен от имени root" - exit 1 -fi - -# Установка зависимостей -echo "📦 Установка зависимостей..." -if command -v apt >/dev/null 2>&1; then - apt update - apt install -y python3 python3-pip git -elif command -v yum >/dev/null 2>&1; then - yum update -y - yum install -y python3 python3-pip git -elif command -v dnf >/dev/null 2>&1; then - dnf update -y - dnf install -y python3 python3-pip git -else - echo "❌ Неподдерживаемая система. Поддерживаются: Ubuntu/Debian/CentOS/RHEL" - exit 1 -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p $INSTALL_DIR -mkdir -p /var/lib/pyguardian -mkdir -p /var/log/pyguardian - -# Клонирование репозитория -echo "⬇️ Клонирование PyGuardian..." -if [ -d "$INSTALL_DIR/.git" ]; then - cd $INSTALL_DIR && git pull -else - git clone $GITHUB_REPO $INSTALL_DIR -fi - -cd $INSTALL_DIR - -# Установка Python зависимостей -echo "🐍 Установка Python пакетов..." -pip3 install -r requirements.txt - -# Настройка systemd сервиса -echo "⚙️ Настройка systemd сервиса..." -cat > /etc/systemd/system/$SERVICE_NAME.service << EOF -[Unit] -Description=PyGuardian Security Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=$INSTALL_DIR -ExecStart=/usr/bin/python3 $INSTALL_DIR/main.py --agent-mode -Restart=always -RestartSec=10 -Environment=PYTHONPATH=$INSTALL_DIR - -[Install] -WantedBy=multi-user.target -EOF - -# Включение и запуск сервиса -echo "🚀 Запуск PyGuardian Agent..." -systemctl daemon-reload -systemctl enable $SERVICE_NAME -systemctl start $SERVICE_NAME - -echo "✅ PyGuardian Agent успешно установлен и запущен!" -echo "📊 Статус: systemctl status $SERVICE_NAME" -echo "📋 Логи: journalctl -u $SERVICE_NAME -f" -''' - - async def load_agents(self) -> None: - """Загрузить конфигурацию агентов""" - try: - if os.path.exists(self.agents_config_path): - async with aiofiles.open(self.agents_config_path, 'r') as f: - content = await f.read() - agents_config = yaml.safe_load(content) - - self.agents = {} - for agent_id, agent_config in agents_config.get('agents', {}).items(): - self.agents[agent_id] = ServerAgent(agent_id, agent_config) - - logger.info(f"Загружено {len(self.agents)} агентов из конфигурации") - else: - logger.info("Файл конфигурации агентов не найден, создаю новый") - await self.save_agents() - - except Exception as e: - logger.error(f"Ошибка загрузки агентов: {e}") - - async def save_agents(self) -> None: - """Сохранить конфигурацию агентов""" - try: - os.makedirs(os.path.dirname(self.agents_config_path), exist_ok=True) - - agents_config = { - 'cluster': { - 'name': self.cluster_name, - 'master_server': self.master_server, - 'last_updated': datetime.now().isoformat() - }, - 'agents': {} - } - - for agent_id, agent in self.agents.items(): - agents_config['agents'][agent_id] = { - 'hostname': agent.hostname, - 'ip_address': agent.ip_address, - 'ssh_port': agent.ssh_port, - 'ssh_user': agent.ssh_user, - 'ssh_key_path': agent.ssh_key_path, - 'status': agent.status, - 'last_check': agent.last_check.isoformat() if agent.last_check else None, - 'version': agent.version - } - - async with aiofiles.open(self.agents_config_path, 'w') as f: - await f.write(yaml.dump(agents_config, default_flow_style=False)) - - logger.info("Конфигурация агентов сохранена") - - except Exception as e: - logger.error(f"Ошибка сохранения агентов: {e}") - - def generate_agent_id(self, hostname: str, ip_address: str) -> str: - """Генерировать уникальный ID для агента""" - return f"{hostname}-{ip_address.replace('.', '-')}" - - async def add_agent(self, hostname: str, ip_address: str, ssh_user: str = 'root', - ssh_port: int = 22, ssh_key_path: str = None, - ssh_password: str = None) -> tuple[bool, str]: - """Добавить новый агент в кластер""" - try: - agent_id = self.generate_agent_id(hostname, ip_address) - - # Проверяем, что агент еще не добавлен - if agent_id in self.agents: - return False, f"Агент {agent_id} уже существует в кластере" - - # Создаем объект агента - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_port': ssh_port, - 'ssh_user': ssh_user, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password - } - - agent = ServerAgent(agent_id, agent_config) - - # Тестируем соединение - connection_test = await self._test_ssh_connection(agent) - if not connection_test[0]: - return False, f"Не удалось подключиться к серверу: {connection_test[1]}" - - # Добавляем агент - self.agents[agent_id] = agent - agent.status = 'added' - agent.last_check = datetime.now() - - # Сохраняем конфигурацию - await self.save_agents() - - # Записываем в базу данных - await self.storage.add_agent(agent_id, agent.to_dict()) - - logger.info(f"Агент {agent_id} успешно добавлен в кластер") - return True, f"Агент {hostname} ({ip_address}) добавлен в кластер" - - except Exception as e: - logger.error(f"Ошибка добавления агента: {e}") - return False, f"Ошибка добавления агента: {e}" - - async def _test_ssh_connection(self, agent: ServerAgent) -> tuple[bool, str]: - """Тестирование SSH соединения с агентом""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не указан метод аутентификации (ключ или пароль)" - - # Тестовая команда - stdin, stdout, stderr = ssh.exec_command('echo "PyGuardian Connection Test"') - result = stdout.read().decode().strip() - - ssh.close() - - if "PyGuardian Connection Test" in result: - return True, "Соединение установлено успешно" - else: - return False, "Тестовая команда не выполнена" - - except Exception as e: - return False, f"Ошибка SSH соединения: {e}" - - async def deploy_agent(self, agent_id: str, force_reinstall: bool = False) -> tuple[bool, str]: - """Развернуть PyGuardian агент на удаленном сервере""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Подключение SSH - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Проверка, установлен ли уже PyGuardian - if not force_reinstall: - stdin, stdout, stderr = ssh.exec_command('systemctl status pyguardian-agent') - if stdout.channel.recv_exit_status() == 0: - agent.status = 'deployed' - await self.save_agents() - ssh.close() - return True, f"PyGuardian уже установлен на {agent.hostname}" - - # Создание временного скрипта развертывания - temp_script = f'/tmp/pyguardian_deploy_{secrets.token_hex(8)}.sh' - - # Загрузка скрипта на сервер - sftp = ssh.open_sftp() - with sftp.open(temp_script, 'w') as f: - f.write(self.deployment_script) - sftp.chmod(temp_script, 0o755) - sftp.close() - - # Выполнение скрипта развертывания - logger.info(f"Начинаю развертывание на {agent.hostname}...") - - stdin, stdout, stderr = ssh.exec_command(f'bash {temp_script}') - - # Получение вывода - deploy_output = stdout.read().decode() - deploy_errors = stderr.read().decode() - exit_status = stdout.channel.recv_exit_status() - - # Удаление временного скрипта - ssh.exec_command(f'rm -f {temp_script}') - - if exit_status == 0: - agent.status = 'deployed' - agent.last_check = datetime.now() - await self.save_agents() - - # Обновляем базу данных - await self.storage.update_agent_status(agent_id, 'deployed') - - ssh.close() - logger.info(f"PyGuardian успешно развернут на {agent.hostname}") - return True, f"PyGuardian успешно установлен на {agent.hostname}" - else: - ssh.close() - logger.error(f"Ошибка развертывания на {agent.hostname}: {deploy_errors}") - return False, f"Ошибка установки: {deploy_errors[:500]}" - - except Exception as e: - logger.error(f"Ошибка развертывания агента {agent_id}: {e}") - return False, f"Ошибка развертывания: {e}" - - async def remove_agent(self, agent_id: str, cleanup_remote: bool = False) -> tuple[bool, str]: - """Удалить агент из кластера""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Удаление с удаленного сервера - if cleanup_remote: - cleanup_result = await self._cleanup_remote_agent(agent) - if not cleanup_result[0]: - logger.warning(f"Не удалось очистить удаленный агент: {cleanup_result[1]}") - - # Удаление из локальной конфигурации - del self.agents[agent_id] - await self.save_agents() - - # Удаление из базы данных - await self.storage.remove_agent(agent_id) - - logger.info(f"Агент {agent_id} удален из кластера") - return True, f"Агент {agent.hostname} удален из кластера" - - except Exception as e: - logger.error(f"Ошибка удаления агента: {e}") - return False, f"Ошибка удаления агента: {e}" - - async def _cleanup_remote_agent(self, agent: ServerAgent) -> tuple[bool, str]: - """Очистка PyGuardian на удаленном сервере""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Команды очистки - cleanup_commands = [ - 'systemctl stop pyguardian-agent', - 'systemctl disable pyguardian-agent', - 'rm -f /etc/systemd/system/pyguardian-agent.service', - 'systemctl daemon-reload', - 'rm -rf /opt/pyguardian', - 'rm -rf /var/lib/pyguardian', - 'rm -f /var/log/pyguardian.log' - ] - - for command in cleanup_commands: - ssh.exec_command(command) - - ssh.close() - return True, "Удаленная очистка выполнена" - - except Exception as e: - return False, f"Ошибка очистки: {e}" - - async def check_agent_status(self, agent_id: str) -> tuple[bool, Dict]: - """Проверить статус агента""" - try: - if agent_id not in self.agents: - return False, {"error": f"Агент {agent_id} не найден"} - - agent = self.agents[agent_id] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, {"error": "Не настроена аутентификация"} - - # Проверка статуса сервиса - stdin, stdout, stderr = ssh.exec_command('systemctl is-active pyguardian-agent') - service_status = stdout.read().decode().strip() - - # Проверка версии - stdin, stdout, stderr = ssh.exec_command('cat /opt/pyguardian/VERSION 2>/dev/null || echo "unknown"') - version = stdout.read().decode().strip() - - # Получение системной информации - stdin, stdout, stderr = ssh.exec_command('uptime && df -h / && free -m') - system_info = stdout.read().decode() - - ssh.close() - - # Обновление информации об агенте - agent.status = 'online' if service_status == 'active' else 'offline' - agent.version = version - agent.last_check = datetime.now() - - status_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "service_status": service_status, - "version": version, - "last_check": agent.last_check.isoformat(), - "system_info": system_info - } - - await self.save_agents() - return True, status_info - - except Exception as e: - logger.error(f"Ошибка проверки статуса агента {agent_id}: {e}") - return False, {"error": f"Ошибка проверки статуса: {e}"} - - async def list_agents(self) -> List[Dict]: - """Получить список всех агентов""" - agents_list = [] - for agent_id, agent in self.agents.items(): - agents_list.append({ - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "last_check": agent.last_check.isoformat() if agent.last_check else None, - "version": agent.version - }) - - return agents_list - - async def get_cluster_stats(self) -> Dict: - """Получить статистику кластера""" - total_agents = len(self.agents) - online_agents = len([a for a in self.agents.values() if a.status == 'online']) - offline_agents = len([a for a in self.agents.values() if a.status == 'offline']) - deployed_agents = len([a for a in self.agents.values() if a.status == 'deployed']) - - return { - "cluster_name": self.cluster_name, - "total_agents": total_agents, - "online_agents": online_agents, - "offline_agents": offline_agents, - "deployed_agents": deployed_agents, - "master_server": self.master_server, - "last_updated": datetime.now().isoformat() - } - - async def check_all_agents(self) -> Dict: - """Проверить статус всех агентов""" - results = { - "checked": 0, - "online": 0, - "offline": 0, - "errors": 0, - "details": [] - } - - for agent_id in self.agents.keys(): - try: - success, status_info = await self.check_agent_status(agent_id) - results["checked"] += 1 - - if success: - if status_info.get("status") == "online": - results["online"] += 1 - else: - results["offline"] += 1 - else: - results["errors"] += 1 - - results["details"].append(status_info) - - except Exception as e: - results["errors"] += 1 - results["details"].append({ - "agent_id": agent_id, - "error": str(e) - }) - - return results \ No newline at end of file diff --git a/.history/src/cluster_manager_20251125203709.py b/.history/src/cluster_manager_20251125203709.py deleted file mode 100644 index e05dac3..0000000 --- a/.history/src/cluster_manager_20251125203709.py +++ /dev/null @@ -1,622 +0,0 @@ -""" -Cluster Manager для PyGuardian -Управление кластером серверов и автоматическое развертывание агентов -""" - -import asyncio -import logging -import json -import subprocess -import os -import yaml -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from pathlib import Path -import aiofiles -import paramiko -from cryptography.fernet import Fernet -import secrets -import string - -logger = logging.getLogger(__name__) - - -class ServerAgent: - """Представление удаленного сервера-агента""" - - def __init__(self, server_id: str, config: Dict): - self.server_id = server_id - self.hostname = config.get('hostname') - self.ip_address = config.get('ip_address') - self.ssh_port = config.get('ssh_port', 22) - self.ssh_user = config.get('ssh_user', 'root') - self.ssh_key_path = config.get('ssh_key_path') - self.ssh_password = config.get('ssh_password') - self.status = 'unknown' - self.last_check = None - self.version = None - self.stats = {} - - def to_dict(self) -> Dict: - """Конвертация в словарь для сериализации""" - return { - 'server_id': self.server_id, - 'hostname': self.hostname, - 'ip_address': self.ip_address, - 'ssh_port': self.ssh_port, - 'ssh_user': self.ssh_user, - 'ssh_key_path': self.ssh_key_path, - 'status': self.status, - 'last_check': self.last_check.isoformat() if self.last_check else None, - 'version': self.version, - 'stats': self.stats - } - - -class ClusterManager: - """Менеджер кластера серверов""" - - def __init__(self, storage, config: Dict): - self.storage = storage - self.config = config - - # Параметры кластера - self.cluster_name = config.get('cluster_name', 'PyGuardian-Cluster') - self.master_server = config.get('master_server', True) - self.agents_config_path = config.get('agents_config_path', '/var/lib/pyguardian/agents.yaml') - self.deployment_path = config.get('deployment_path', '/opt/pyguardian') - - # SSH настройки - self.ssh_timeout = config.get('ssh_timeout', 30) - self.ssh_retries = config.get('ssh_retries', 3) - - # Шифрование - self.encryption_key = self._get_or_create_cluster_key() - self.cipher = Fernet(self.encryption_key) - - # Кэш агентов - self.agents: Dict[str, ServerAgent] = {} - - # Шаблоны для развертывания - self.deployment_script = self._get_deployment_script() - - def _get_or_create_cluster_key(self) -> bytes: - """Получить или создать ключ шифрования кластера""" - key_file = "/var/lib/pyguardian/cluster_encryption.key" - try: - os.makedirs(os.path.dirname(key_file), exist_ok=True) - - if os.path.exists(key_file): - with open(key_file, 'rb') as f: - return f.read() - else: - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) - logger.info("Создан новый ключ шифрования кластера") - return key - except Exception as e: - logger.error(f"Ошибка работы с ключом кластера: {e}") - return Fernet.generate_key() - - def _get_deployment_script(self) -> str: - """Получить скрипт развертывания агента""" - return '''#!/bin/bash -# PyGuardian Agent Deployment Script -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_NAME="pyguardian-agent" -GITHUB_REPO="https://github.com/your-repo/PyGuardian.git" - -echo "🛡️ Начинаю установку PyGuardian Agent..." - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Скрипт должен быть запущен от имени root" - exit 1 -fi - -# Установка зависимостей -echo "📦 Установка зависимостей..." -if command -v apt >/dev/null 2>&1; then - apt update - apt install -y python3 python3-pip git -elif command -v yum >/dev/null 2>&1; then - yum update -y - yum install -y python3 python3-pip git -elif command -v dnf >/dev/null 2>&1; then - dnf update -y - dnf install -y python3 python3-pip git -else - echo "❌ Неподдерживаемая система. Поддерживаются: Ubuntu/Debian/CentOS/RHEL" - exit 1 -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p $INSTALL_DIR -mkdir -p /var/lib/pyguardian -mkdir -p /var/log/pyguardian - -# Клонирование репозитория -echo "⬇️ Клонирование PyGuardian..." -if [ -d "$INSTALL_DIR/.git" ]; then - cd $INSTALL_DIR && git pull -else - git clone $GITHUB_REPO $INSTALL_DIR -fi - -cd $INSTALL_DIR - -# Установка Python зависимостей -echo "🐍 Установка Python пакетов..." -pip3 install -r requirements.txt - -# Настройка systemd сервиса -echo "⚙️ Настройка systemd сервиса..." -cat > /etc/systemd/system/$SERVICE_NAME.service << EOF -[Unit] -Description=PyGuardian Security Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=$INSTALL_DIR -ExecStart=/usr/bin/python3 $INSTALL_DIR/main.py --agent-mode -Restart=always -RestartSec=10 -Environment=PYTHONPATH=$INSTALL_DIR - -[Install] -WantedBy=multi-user.target -EOF - -# Включение и запуск сервиса -echo "🚀 Запуск PyGuardian Agent..." -systemctl daemon-reload -systemctl enable $SERVICE_NAME -systemctl start $SERVICE_NAME - -echo "✅ PyGuardian Agent успешно установлен и запущен!" -echo "📊 Статус: systemctl status $SERVICE_NAME" -echo "📋 Логи: journalctl -u $SERVICE_NAME -f" -''' - - async def load_agents(self) -> None: - """Загрузить конфигурацию агентов""" - try: - if os.path.exists(self.agents_config_path): - async with aiofiles.open(self.agents_config_path, 'r') as f: - content = await f.read() - agents_config = yaml.safe_load(content) - - self.agents = {} - for agent_id, agent_config in agents_config.get('agents', {}).items(): - self.agents[agent_id] = ServerAgent(agent_id, agent_config) - - logger.info(f"Загружено {len(self.agents)} агентов из конфигурации") - else: - logger.info("Файл конфигурации агентов не найден, создаю новый") - await self.save_agents() - - except Exception as e: - logger.error(f"Ошибка загрузки агентов: {e}") - - async def save_agents(self) -> None: - """Сохранить конфигурацию агентов""" - try: - os.makedirs(os.path.dirname(self.agents_config_path), exist_ok=True) - - agents_config = { - 'cluster': { - 'name': self.cluster_name, - 'master_server': self.master_server, - 'last_updated': datetime.now().isoformat() - }, - 'agents': {} - } - - for agent_id, agent in self.agents.items(): - agents_config['agents'][agent_id] = { - 'hostname': agent.hostname, - 'ip_address': agent.ip_address, - 'ssh_port': agent.ssh_port, - 'ssh_user': agent.ssh_user, - 'ssh_key_path': agent.ssh_key_path, - 'status': agent.status, - 'last_check': agent.last_check.isoformat() if agent.last_check else None, - 'version': agent.version - } - - async with aiofiles.open(self.agents_config_path, 'w') as f: - await f.write(yaml.dump(agents_config, default_flow_style=False)) - - logger.info("Конфигурация агентов сохранена") - - except Exception as e: - logger.error(f"Ошибка сохранения агентов: {e}") - - def generate_agent_id(self, hostname: str, ip_address: str) -> str: - """Генерировать уникальный ID для агента""" - return f"{hostname}-{ip_address.replace('.', '-')}" - - async def add_agent(self, hostname: str, ip_address: str, ssh_user: str = 'root', - ssh_port: int = 22, ssh_key_path: str = None, - ssh_password: str = None) -> tuple[bool, str]: - """Добавить новый агент в кластер""" - try: - agent_id = self.generate_agent_id(hostname, ip_address) - - # Проверяем, что агент еще не добавлен - if agent_id in self.agents: - return False, f"Агент {agent_id} уже существует в кластере" - - # Создаем объект агента - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_port': ssh_port, - 'ssh_user': ssh_user, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password - } - - agent = ServerAgent(agent_id, agent_config) - - # Тестируем соединение - connection_test = await self._test_ssh_connection(agent) - if not connection_test[0]: - return False, f"Не удалось подключиться к серверу: {connection_test[1]}" - - # Добавляем агент - self.agents[agent_id] = agent - agent.status = 'added' - agent.last_check = datetime.now() - - # Сохраняем конфигурацию - await self.save_agents() - - # Записываем в базу данных - await self.storage.add_agent(agent_id, agent.to_dict()) - - logger.info(f"Агент {agent_id} успешно добавлен в кластер") - return True, f"Агент {hostname} ({ip_address}) добавлен в кластер" - - except Exception as e: - logger.error(f"Ошибка добавления агента: {e}") - return False, f"Ошибка добавления агента: {e}" - - async def _test_ssh_connection(self, agent: ServerAgent) -> tuple[bool, str]: - """Тестирование SSH соединения с агентом""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не указан метод аутентификации (ключ или пароль)" - - # Тестовая команда - stdin, stdout, stderr = ssh.exec_command('echo "PyGuardian Connection Test"') - result = stdout.read().decode().strip() - - ssh.close() - - if "PyGuardian Connection Test" in result: - return True, "Соединение установлено успешно" - else: - return False, "Тестовая команда не выполнена" - - except Exception as e: - return False, f"Ошибка SSH соединения: {e}" - - async def deploy_agent(self, agent_id: str, force_reinstall: bool = False) -> tuple[bool, str]: - """Развернуть PyGuardian агент на удаленном сервере""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Подключение SSH - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Проверка, установлен ли уже PyGuardian - if not force_reinstall: - stdin, stdout, stderr = ssh.exec_command('systemctl status pyguardian-agent') - if stdout.channel.recv_exit_status() == 0: - agent.status = 'deployed' - await self.save_agents() - ssh.close() - return True, f"PyGuardian уже установлен на {agent.hostname}" - - # Создание временного скрипта развертывания - temp_script = f'/tmp/pyguardian_deploy_{secrets.token_hex(8)}.sh' - - # Загрузка скрипта на сервер - sftp = ssh.open_sftp() - with sftp.open(temp_script, 'w') as f: - f.write(self.deployment_script) - sftp.chmod(temp_script, 0o755) - sftp.close() - - # Выполнение скрипта развертывания - logger.info(f"Начинаю развертывание на {agent.hostname}...") - - stdin, stdout, stderr = ssh.exec_command(f'bash {temp_script}') - - # Получение вывода - deploy_output = stdout.read().decode() - deploy_errors = stderr.read().decode() - exit_status = stdout.channel.recv_exit_status() - - # Удаление временного скрипта - ssh.exec_command(f'rm -f {temp_script}') - - if exit_status == 0: - agent.status = 'deployed' - agent.last_check = datetime.now() - await self.save_agents() - - # Обновляем базу данных - await self.storage.update_agent_status(agent_id, 'deployed') - - ssh.close() - logger.info(f"PyGuardian успешно развернут на {agent.hostname}") - return True, f"PyGuardian успешно установлен на {agent.hostname}" - else: - ssh.close() - logger.error(f"Ошибка развертывания на {agent.hostname}: {deploy_errors}") - return False, f"Ошибка установки: {deploy_errors[:500]}" - - except Exception as e: - logger.error(f"Ошибка развертывания агента {agent_id}: {e}") - return False, f"Ошибка развертывания: {e}" - - async def remove_agent(self, agent_id: str, cleanup_remote: bool = False) -> tuple[bool, str]: - """Удалить агент из кластера""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Удаление с удаленного сервера - if cleanup_remote: - cleanup_result = await self._cleanup_remote_agent(agent) - if not cleanup_result[0]: - logger.warning(f"Не удалось очистить удаленный агент: {cleanup_result[1]}") - - # Удаление из локальной конфигурации - del self.agents[agent_id] - await self.save_agents() - - # Удаление из базы данных - await self.storage.remove_agent(agent_id) - - logger.info(f"Агент {agent_id} удален из кластера") - return True, f"Агент {agent.hostname} удален из кластера" - - except Exception as e: - logger.error(f"Ошибка удаления агента: {e}") - return False, f"Ошибка удаления агента: {e}" - - async def _cleanup_remote_agent(self, agent: ServerAgent) -> tuple[bool, str]: - """Очистка PyGuardian на удаленном сервере""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Команды очистки - cleanup_commands = [ - 'systemctl stop pyguardian-agent', - 'systemctl disable pyguardian-agent', - 'rm -f /etc/systemd/system/pyguardian-agent.service', - 'systemctl daemon-reload', - 'rm -rf /opt/pyguardian', - 'rm -rf /var/lib/pyguardian', - 'rm -f /var/log/pyguardian.log' - ] - - for command in cleanup_commands: - ssh.exec_command(command) - - ssh.close() - return True, "Удаленная очистка выполнена" - - except Exception as e: - return False, f"Ошибка очистки: {e}" - - async def check_agent_status(self, agent_id: str) -> tuple[bool, Dict]: - """Проверить статус агента""" - try: - if agent_id not in self.agents: - return False, {"error": f"Агент {agent_id} не найден"} - - agent = self.agents[agent_id] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, {"error": "Не настроена аутентификация"} - - # Проверка статуса сервиса - stdin, stdout, stderr = ssh.exec_command('systemctl is-active pyguardian-agent') - service_status = stdout.read().decode().strip() - - # Проверка версии - stdin, stdout, stderr = ssh.exec_command('cat /opt/pyguardian/VERSION 2>/dev/null || echo "unknown"') - version = stdout.read().decode().strip() - - # Получение системной информации - stdin, stdout, stderr = ssh.exec_command('uptime && df -h / && free -m') - system_info = stdout.read().decode() - - ssh.close() - - # Обновление информации об агенте - agent.status = 'online' if service_status == 'active' else 'offline' - agent.version = version - agent.last_check = datetime.now() - - status_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "service_status": service_status, - "version": version, - "last_check": agent.last_check.isoformat(), - "system_info": system_info - } - - await self.save_agents() - return True, status_info - - except Exception as e: - logger.error(f"Ошибка проверки статуса агента {agent_id}: {e}") - return False, {"error": f"Ошибка проверки статуса: {e}"} - - async def list_agents(self) -> List[Dict]: - """Получить список всех агентов""" - agents_list = [] - for agent_id, agent in self.agents.items(): - agents_list.append({ - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "last_check": agent.last_check.isoformat() if agent.last_check else None, - "version": agent.version - }) - - return agents_list - - async def get_cluster_stats(self) -> Dict: - """Получить статистику кластера""" - total_agents = len(self.agents) - online_agents = len([a for a in self.agents.values() if a.status == 'online']) - offline_agents = len([a for a in self.agents.values() if a.status == 'offline']) - deployed_agents = len([a for a in self.agents.values() if a.status == 'deployed']) - - return { - "cluster_name": self.cluster_name, - "total_agents": total_agents, - "online_agents": online_agents, - "offline_agents": offline_agents, - "deployed_agents": deployed_agents, - "master_server": self.master_server, - "last_updated": datetime.now().isoformat() - } - - async def check_all_agents(self) -> Dict: - """Проверить статус всех агентов""" - results = { - "checked": 0, - "online": 0, - "offline": 0, - "errors": 0, - "details": [] - } - - for agent_id in self.agents.keys(): - try: - success, status_info = await self.check_agent_status(agent_id) - results["checked"] += 1 - - if success: - if status_info.get("status") == "online": - results["online"] += 1 - else: - results["offline"] += 1 - else: - results["errors"] += 1 - - results["details"].append(status_info) - - except Exception as e: - results["errors"] += 1 - results["details"].append({ - "agent_id": agent_id, - "error": str(e) - }) - - return results \ No newline at end of file diff --git a/.history/src/cluster_manager_20251125205552.py b/.history/src/cluster_manager_20251125205552.py deleted file mode 100644 index fad1c1c..0000000 --- a/.history/src/cluster_manager_20251125205552.py +++ /dev/null @@ -1,640 +0,0 @@ -""" -Cluster Manager для PyGuardian -Управление кластером серверов и автоматическое развертывание агентов -""" - -import asyncio -import logging -import json -import subprocess -import os -import yaml -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from pathlib import Path -import aiofiles -import paramiko -from cryptography.fernet import Fernet -import secrets -import string -import hashlib - -# Импортируем систему аутентификации -from .auth import AgentAuthentication, AgentAuthenticationError -from .storage import Storage - -logger = logging.getLogger(__name__) - - -class ServerAgent: - """Представление удаленного сервера-агента""" - - def __init__(self, server_id: str, config: Dict): - self.server_id = server_id - self.hostname = config.get('hostname') - self.ip_address = config.get('ip_address') - self.ssh_port = config.get('ssh_port', 22) - self.ssh_user = config.get('ssh_user', 'root') - self.ssh_key_path = config.get('ssh_key_path') - self.ssh_password = config.get('ssh_password') - self.status = 'unknown' - self.last_check = None - self.version = None - self.stats = {} - - # Новые поля для аутентификации - self.agent_id = config.get('agent_id') - self.secret_key = config.get('secret_key') - self.access_token = config.get('access_token') - self.refresh_token = config.get('refresh_token') - self.token_expires_at = config.get('token_expires_at') - self.last_authenticated = config.get('last_authenticated') - self.is_authenticated = False - - def to_dict(self) -> Dict: - """Конвертация в словарь для сериализации""" - return { - 'server_id': self.server_id, - 'hostname': self.hostname, - 'ip_address': self.ip_address, - 'ssh_port': self.ssh_port, - 'ssh_user': self.ssh_user, - 'ssh_key_path': self.ssh_key_path, - 'status': self.status, - 'last_check': self.last_check.isoformat() if self.last_check else None, - 'version': self.version, - 'agent_id': self.agent_id, - 'is_authenticated': self.is_authenticated, - 'last_authenticated': self.last_authenticated, - 'token_expires_at': self.token_expires_at, - 'stats': self.stats - } - - -class ClusterManager: - """Менеджер кластера серверов""" - - def __init__(self, storage, config: Dict): - self.storage = storage - self.config = config - - # Параметры кластера - self.cluster_name = config.get('cluster_name', 'PyGuardian-Cluster') - self.master_server = config.get('master_server', True) - self.agents_config_path = config.get('agents_config_path', '/var/lib/pyguardian/agents.yaml') - self.deployment_path = config.get('deployment_path', '/opt/pyguardian') - - # SSH настройки - self.ssh_timeout = config.get('ssh_timeout', 30) - self.ssh_retries = config.get('ssh_retries', 3) - - # Шифрование - self.encryption_key = self._get_or_create_cluster_key() - self.cipher = Fernet(self.encryption_key) - - # Кэш агентов - self.agents: Dict[str, ServerAgent] = {} - - # Шаблоны для развертывания - self.deployment_script = self._get_deployment_script() - - def _get_or_create_cluster_key(self) -> bytes: - """Получить или создать ключ шифрования кластера""" - key_file = "/var/lib/pyguardian/cluster_encryption.key" - try: - os.makedirs(os.path.dirname(key_file), exist_ok=True) - - if os.path.exists(key_file): - with open(key_file, 'rb') as f: - return f.read() - else: - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) - logger.info("Создан новый ключ шифрования кластера") - return key - except Exception as e: - logger.error(f"Ошибка работы с ключом кластера: {e}") - return Fernet.generate_key() - - def _get_deployment_script(self) -> str: - """Получить скрипт развертывания агента""" - return '''#!/bin/bash -# PyGuardian Agent Deployment Script -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_NAME="pyguardian-agent" -GITHUB_REPO="https://github.com/your-repo/PyGuardian.git" - -echo "🛡️ Начинаю установку PyGuardian Agent..." - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Скрипт должен быть запущен от имени root" - exit 1 -fi - -# Установка зависимостей -echo "📦 Установка зависимостей..." -if command -v apt >/dev/null 2>&1; then - apt update - apt install -y python3 python3-pip git -elif command -v yum >/dev/null 2>&1; then - yum update -y - yum install -y python3 python3-pip git -elif command -v dnf >/dev/null 2>&1; then - dnf update -y - dnf install -y python3 python3-pip git -else - echo "❌ Неподдерживаемая система. Поддерживаются: Ubuntu/Debian/CentOS/RHEL" - exit 1 -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p $INSTALL_DIR -mkdir -p /var/lib/pyguardian -mkdir -p /var/log/pyguardian - -# Клонирование репозитория -echo "⬇️ Клонирование PyGuardian..." -if [ -d "$INSTALL_DIR/.git" ]; then - cd $INSTALL_DIR && git pull -else - git clone $GITHUB_REPO $INSTALL_DIR -fi - -cd $INSTALL_DIR - -# Установка Python зависимостей -echo "🐍 Установка Python пакетов..." -pip3 install -r requirements.txt - -# Настройка systemd сервиса -echo "⚙️ Настройка systemd сервиса..." -cat > /etc/systemd/system/$SERVICE_NAME.service << EOF -[Unit] -Description=PyGuardian Security Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=$INSTALL_DIR -ExecStart=/usr/bin/python3 $INSTALL_DIR/main.py --agent-mode -Restart=always -RestartSec=10 -Environment=PYTHONPATH=$INSTALL_DIR - -[Install] -WantedBy=multi-user.target -EOF - -# Включение и запуск сервиса -echo "🚀 Запуск PyGuardian Agent..." -systemctl daemon-reload -systemctl enable $SERVICE_NAME -systemctl start $SERVICE_NAME - -echo "✅ PyGuardian Agent успешно установлен и запущен!" -echo "📊 Статус: systemctl status $SERVICE_NAME" -echo "📋 Логи: journalctl -u $SERVICE_NAME -f" -''' - - async def load_agents(self) -> None: - """Загрузить конфигурацию агентов""" - try: - if os.path.exists(self.agents_config_path): - async with aiofiles.open(self.agents_config_path, 'r') as f: - content = await f.read() - agents_config = yaml.safe_load(content) - - self.agents = {} - for agent_id, agent_config in agents_config.get('agents', {}).items(): - self.agents[agent_id] = ServerAgent(agent_id, agent_config) - - logger.info(f"Загружено {len(self.agents)} агентов из конфигурации") - else: - logger.info("Файл конфигурации агентов не найден, создаю новый") - await self.save_agents() - - except Exception as e: - logger.error(f"Ошибка загрузки агентов: {e}") - - async def save_agents(self) -> None: - """Сохранить конфигурацию агентов""" - try: - os.makedirs(os.path.dirname(self.agents_config_path), exist_ok=True) - - agents_config = { - 'cluster': { - 'name': self.cluster_name, - 'master_server': self.master_server, - 'last_updated': datetime.now().isoformat() - }, - 'agents': {} - } - - for agent_id, agent in self.agents.items(): - agents_config['agents'][agent_id] = { - 'hostname': agent.hostname, - 'ip_address': agent.ip_address, - 'ssh_port': agent.ssh_port, - 'ssh_user': agent.ssh_user, - 'ssh_key_path': agent.ssh_key_path, - 'status': agent.status, - 'last_check': agent.last_check.isoformat() if agent.last_check else None, - 'version': agent.version - } - - async with aiofiles.open(self.agents_config_path, 'w') as f: - await f.write(yaml.dump(agents_config, default_flow_style=False)) - - logger.info("Конфигурация агентов сохранена") - - except Exception as e: - logger.error(f"Ошибка сохранения агентов: {e}") - - def generate_agent_id(self, hostname: str, ip_address: str) -> str: - """Генерировать уникальный ID для агента""" - return f"{hostname}-{ip_address.replace('.', '-')}" - - async def add_agent(self, hostname: str, ip_address: str, ssh_user: str = 'root', - ssh_port: int = 22, ssh_key_path: str = None, - ssh_password: str = None) -> tuple[bool, str]: - """Добавить новый агент в кластер""" - try: - agent_id = self.generate_agent_id(hostname, ip_address) - - # Проверяем, что агент еще не добавлен - if agent_id in self.agents: - return False, f"Агент {agent_id} уже существует в кластере" - - # Создаем объект агента - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_port': ssh_port, - 'ssh_user': ssh_user, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password - } - - agent = ServerAgent(agent_id, agent_config) - - # Тестируем соединение - connection_test = await self._test_ssh_connection(agent) - if not connection_test[0]: - return False, f"Не удалось подключиться к серверу: {connection_test[1]}" - - # Добавляем агент - self.agents[agent_id] = agent - agent.status = 'added' - agent.last_check = datetime.now() - - # Сохраняем конфигурацию - await self.save_agents() - - # Записываем в базу данных - await self.storage.add_agent(agent_id, agent.to_dict()) - - logger.info(f"Агент {agent_id} успешно добавлен в кластер") - return True, f"Агент {hostname} ({ip_address}) добавлен в кластер" - - except Exception as e: - logger.error(f"Ошибка добавления агента: {e}") - return False, f"Ошибка добавления агента: {e}" - - async def _test_ssh_connection(self, agent: ServerAgent) -> tuple[bool, str]: - """Тестирование SSH соединения с агентом""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не указан метод аутентификации (ключ или пароль)" - - # Тестовая команда - stdin, stdout, stderr = ssh.exec_command('echo "PyGuardian Connection Test"') - result = stdout.read().decode().strip() - - ssh.close() - - if "PyGuardian Connection Test" in result: - return True, "Соединение установлено успешно" - else: - return False, "Тестовая команда не выполнена" - - except Exception as e: - return False, f"Ошибка SSH соединения: {e}" - - async def deploy_agent(self, agent_id: str, force_reinstall: bool = False) -> tuple[bool, str]: - """Развернуть PyGuardian агент на удаленном сервере""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Подключение SSH - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Проверка, установлен ли уже PyGuardian - if not force_reinstall: - stdin, stdout, stderr = ssh.exec_command('systemctl status pyguardian-agent') - if stdout.channel.recv_exit_status() == 0: - agent.status = 'deployed' - await self.save_agents() - ssh.close() - return True, f"PyGuardian уже установлен на {agent.hostname}" - - # Создание временного скрипта развертывания - temp_script = f'/tmp/pyguardian_deploy_{secrets.token_hex(8)}.sh' - - # Загрузка скрипта на сервер - sftp = ssh.open_sftp() - with sftp.open(temp_script, 'w') as f: - f.write(self.deployment_script) - sftp.chmod(temp_script, 0o755) - sftp.close() - - # Выполнение скрипта развертывания - logger.info(f"Начинаю развертывание на {agent.hostname}...") - - stdin, stdout, stderr = ssh.exec_command(f'bash {temp_script}') - - # Получение вывода - deploy_output = stdout.read().decode() - deploy_errors = stderr.read().decode() - exit_status = stdout.channel.recv_exit_status() - - # Удаление временного скрипта - ssh.exec_command(f'rm -f {temp_script}') - - if exit_status == 0: - agent.status = 'deployed' - agent.last_check = datetime.now() - await self.save_agents() - - # Обновляем базу данных - await self.storage.update_agent_status(agent_id, 'deployed') - - ssh.close() - logger.info(f"PyGuardian успешно развернут на {agent.hostname}") - return True, f"PyGuardian успешно установлен на {agent.hostname}" - else: - ssh.close() - logger.error(f"Ошибка развертывания на {agent.hostname}: {deploy_errors}") - return False, f"Ошибка установки: {deploy_errors[:500]}" - - except Exception as e: - logger.error(f"Ошибка развертывания агента {agent_id}: {e}") - return False, f"Ошибка развертывания: {e}" - - async def remove_agent(self, agent_id: str, cleanup_remote: bool = False) -> tuple[bool, str]: - """Удалить агент из кластера""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Удаление с удаленного сервера - if cleanup_remote: - cleanup_result = await self._cleanup_remote_agent(agent) - if not cleanup_result[0]: - logger.warning(f"Не удалось очистить удаленный агент: {cleanup_result[1]}") - - # Удаление из локальной конфигурации - del self.agents[agent_id] - await self.save_agents() - - # Удаление из базы данных - await self.storage.remove_agent(agent_id) - - logger.info(f"Агент {agent_id} удален из кластера") - return True, f"Агент {agent.hostname} удален из кластера" - - except Exception as e: - logger.error(f"Ошибка удаления агента: {e}") - return False, f"Ошибка удаления агента: {e}" - - async def _cleanup_remote_agent(self, agent: ServerAgent) -> tuple[bool, str]: - """Очистка PyGuardian на удаленном сервере""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Команды очистки - cleanup_commands = [ - 'systemctl stop pyguardian-agent', - 'systemctl disable pyguardian-agent', - 'rm -f /etc/systemd/system/pyguardian-agent.service', - 'systemctl daemon-reload', - 'rm -rf /opt/pyguardian', - 'rm -rf /var/lib/pyguardian', - 'rm -f /var/log/pyguardian.log' - ] - - for command in cleanup_commands: - ssh.exec_command(command) - - ssh.close() - return True, "Удаленная очистка выполнена" - - except Exception as e: - return False, f"Ошибка очистки: {e}" - - async def check_agent_status(self, agent_id: str) -> tuple[bool, Dict]: - """Проверить статус агента""" - try: - if agent_id not in self.agents: - return False, {"error": f"Агент {agent_id} не найден"} - - agent = self.agents[agent_id] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, {"error": "Не настроена аутентификация"} - - # Проверка статуса сервиса - stdin, stdout, stderr = ssh.exec_command('systemctl is-active pyguardian-agent') - service_status = stdout.read().decode().strip() - - # Проверка версии - stdin, stdout, stderr = ssh.exec_command('cat /opt/pyguardian/VERSION 2>/dev/null || echo "unknown"') - version = stdout.read().decode().strip() - - # Получение системной информации - stdin, stdout, stderr = ssh.exec_command('uptime && df -h / && free -m') - system_info = stdout.read().decode() - - ssh.close() - - # Обновление информации об агенте - agent.status = 'online' if service_status == 'active' else 'offline' - agent.version = version - agent.last_check = datetime.now() - - status_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "service_status": service_status, - "version": version, - "last_check": agent.last_check.isoformat(), - "system_info": system_info - } - - await self.save_agents() - return True, status_info - - except Exception as e: - logger.error(f"Ошибка проверки статуса агента {agent_id}: {e}") - return False, {"error": f"Ошибка проверки статуса: {e}"} - - async def list_agents(self) -> List[Dict]: - """Получить список всех агентов""" - agents_list = [] - for agent_id, agent in self.agents.items(): - agents_list.append({ - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "last_check": agent.last_check.isoformat() if agent.last_check else None, - "version": agent.version - }) - - return agents_list - - async def get_cluster_stats(self) -> Dict: - """Получить статистику кластера""" - total_agents = len(self.agents) - online_agents = len([a for a in self.agents.values() if a.status == 'online']) - offline_agents = len([a for a in self.agents.values() if a.status == 'offline']) - deployed_agents = len([a for a in self.agents.values() if a.status == 'deployed']) - - return { - "cluster_name": self.cluster_name, - "total_agents": total_agents, - "online_agents": online_agents, - "offline_agents": offline_agents, - "deployed_agents": deployed_agents, - "master_server": self.master_server, - "last_updated": datetime.now().isoformat() - } - - async def check_all_agents(self) -> Dict: - """Проверить статус всех агентов""" - results = { - "checked": 0, - "online": 0, - "offline": 0, - "errors": 0, - "details": [] - } - - for agent_id in self.agents.keys(): - try: - success, status_info = await self.check_agent_status(agent_id) - results["checked"] += 1 - - if success: - if status_info.get("status") == "online": - results["online"] += 1 - else: - results["offline"] += 1 - else: - results["errors"] += 1 - - results["details"].append(status_info) - - except Exception as e: - results["errors"] += 1 - results["details"].append({ - "agent_id": agent_id, - "error": str(e) - }) - - return results \ No newline at end of file diff --git a/.history/src/cluster_manager_20251125205610.py b/.history/src/cluster_manager_20251125205610.py deleted file mode 100644 index de00594..0000000 --- a/.history/src/cluster_manager_20251125205610.py +++ /dev/null @@ -1,653 +0,0 @@ -""" -Cluster Manager для PyGuardian -Управление кластером серверов и автоматическое развертывание агентов -""" - -import asyncio -import logging -import json -import subprocess -import os -import yaml -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from pathlib import Path -import aiofiles -import paramiko -from cryptography.fernet import Fernet -import secrets -import string -import hashlib - -# Импортируем систему аутентификации -from .auth import AgentAuthentication, AgentAuthenticationError -from .storage import Storage - -logger = logging.getLogger(__name__) - - -class ServerAgent: - """Представление удаленного сервера-агента""" - - def __init__(self, server_id: str, config: Dict): - self.server_id = server_id - self.hostname = config.get('hostname') - self.ip_address = config.get('ip_address') - self.ssh_port = config.get('ssh_port', 22) - self.ssh_user = config.get('ssh_user', 'root') - self.ssh_key_path = config.get('ssh_key_path') - self.ssh_password = config.get('ssh_password') - self.status = 'unknown' - self.last_check = None - self.version = None - self.stats = {} - - # Новые поля для аутентификации - self.agent_id = config.get('agent_id') - self.secret_key = config.get('secret_key') - self.access_token = config.get('access_token') - self.refresh_token = config.get('refresh_token') - self.token_expires_at = config.get('token_expires_at') - self.last_authenticated = config.get('last_authenticated') - self.is_authenticated = False - - def to_dict(self) -> Dict: - """Конвертация в словарь для сериализации""" - return { - 'server_id': self.server_id, - 'hostname': self.hostname, - 'ip_address': self.ip_address, - 'ssh_port': self.ssh_port, - 'ssh_user': self.ssh_user, - 'ssh_key_path': self.ssh_key_path, - 'status': self.status, - 'last_check': self.last_check.isoformat() if self.last_check else None, - 'version': self.version, - 'agent_id': self.agent_id, - 'is_authenticated': self.is_authenticated, - 'last_authenticated': self.last_authenticated, - 'token_expires_at': self.token_expires_at, - 'stats': self.stats - } - - -class ClusterManager: - """Менеджер кластера серверов""" - - def __init__(self, storage: Storage, config: Dict): - self.storage = storage - self.config = config - - # Параметры кластера - self.cluster_name = config.get('cluster_name', 'PyGuardian-Cluster') - self.master_server = config.get('master_server', True) - self.agents_config_path = config.get('agents_config_path', '/var/lib/pyguardian/agents.yaml') - self.deployment_path = config.get('deployment_path', '/opt/pyguardian') - - # SSH настройки - self.ssh_timeout = config.get('ssh_timeout', 30) - self.ssh_retries = config.get('ssh_retries', 3) - - # Шифрование - self.encryption_key = self._get_or_create_cluster_key() - self.cipher = Fernet(self.encryption_key) - - # Инициализация системы аутентификации - cluster_secret = config.get('cluster_secret', self._generate_cluster_secret()) - self.auth_manager = AgentAuthentication( - secret_key=cluster_secret, - token_expiry_minutes=config.get('token_expiry_minutes', 30) - ) - - # Кэш агентов - self.agents: Dict[str, ServerAgent] = {} - - # Шаблоны для развертывания - self.deployment_script = self._get_deployment_script() - - def _generate_cluster_secret(self) -> str: - """Генерация секрета кластера если он не задан""" - secret = secrets.token_urlsafe(64) - logger.warning(f"Generated new cluster secret. Add to config: cluster_secret: {secret}") - return secret - - def _get_or_create_cluster_key(self) -> bytes: - """Получить или создать ключ шифрования кластера""" - key_file = "/var/lib/pyguardian/cluster_encryption.key" - try: - os.makedirs(os.path.dirname(key_file), exist_ok=True) - - if os.path.exists(key_file): - with open(key_file, 'rb') as f: - return f.read() - else: - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) - logger.info("Создан новый ключ шифрования кластера") - return key - except Exception as e: - logger.error(f"Ошибка работы с ключом кластера: {e}") - return Fernet.generate_key() - - def _get_deployment_script(self) -> str: - """Получить скрипт развертывания агента""" - return '''#!/bin/bash -# PyGuardian Agent Deployment Script -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_NAME="pyguardian-agent" -GITHUB_REPO="https://github.com/your-repo/PyGuardian.git" - -echo "🛡️ Начинаю установку PyGuardian Agent..." - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Скрипт должен быть запущен от имени root" - exit 1 -fi - -# Установка зависимостей -echo "📦 Установка зависимостей..." -if command -v apt >/dev/null 2>&1; then - apt update - apt install -y python3 python3-pip git -elif command -v yum >/dev/null 2>&1; then - yum update -y - yum install -y python3 python3-pip git -elif command -v dnf >/dev/null 2>&1; then - dnf update -y - dnf install -y python3 python3-pip git -else - echo "❌ Неподдерживаемая система. Поддерживаются: Ubuntu/Debian/CentOS/RHEL" - exit 1 -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p $INSTALL_DIR -mkdir -p /var/lib/pyguardian -mkdir -p /var/log/pyguardian - -# Клонирование репозитория -echo "⬇️ Клонирование PyGuardian..." -if [ -d "$INSTALL_DIR/.git" ]; then - cd $INSTALL_DIR && git pull -else - git clone $GITHUB_REPO $INSTALL_DIR -fi - -cd $INSTALL_DIR - -# Установка Python зависимостей -echo "🐍 Установка Python пакетов..." -pip3 install -r requirements.txt - -# Настройка systemd сервиса -echo "⚙️ Настройка systemd сервиса..." -cat > /etc/systemd/system/$SERVICE_NAME.service << EOF -[Unit] -Description=PyGuardian Security Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=$INSTALL_DIR -ExecStart=/usr/bin/python3 $INSTALL_DIR/main.py --agent-mode -Restart=always -RestartSec=10 -Environment=PYTHONPATH=$INSTALL_DIR - -[Install] -WantedBy=multi-user.target -EOF - -# Включение и запуск сервиса -echo "🚀 Запуск PyGuardian Agent..." -systemctl daemon-reload -systemctl enable $SERVICE_NAME -systemctl start $SERVICE_NAME - -echo "✅ PyGuardian Agent успешно установлен и запущен!" -echo "📊 Статус: systemctl status $SERVICE_NAME" -echo "📋 Логи: journalctl -u $SERVICE_NAME -f" -''' - - async def load_agents(self) -> None: - """Загрузить конфигурацию агентов""" - try: - if os.path.exists(self.agents_config_path): - async with aiofiles.open(self.agents_config_path, 'r') as f: - content = await f.read() - agents_config = yaml.safe_load(content) - - self.agents = {} - for agent_id, agent_config in agents_config.get('agents', {}).items(): - self.agents[agent_id] = ServerAgent(agent_id, agent_config) - - logger.info(f"Загружено {len(self.agents)} агентов из конфигурации") - else: - logger.info("Файл конфигурации агентов не найден, создаю новый") - await self.save_agents() - - except Exception as e: - logger.error(f"Ошибка загрузки агентов: {e}") - - async def save_agents(self) -> None: - """Сохранить конфигурацию агентов""" - try: - os.makedirs(os.path.dirname(self.agents_config_path), exist_ok=True) - - agents_config = { - 'cluster': { - 'name': self.cluster_name, - 'master_server': self.master_server, - 'last_updated': datetime.now().isoformat() - }, - 'agents': {} - } - - for agent_id, agent in self.agents.items(): - agents_config['agents'][agent_id] = { - 'hostname': agent.hostname, - 'ip_address': agent.ip_address, - 'ssh_port': agent.ssh_port, - 'ssh_user': agent.ssh_user, - 'ssh_key_path': agent.ssh_key_path, - 'status': agent.status, - 'last_check': agent.last_check.isoformat() if agent.last_check else None, - 'version': agent.version - } - - async with aiofiles.open(self.agents_config_path, 'w') as f: - await f.write(yaml.dump(agents_config, default_flow_style=False)) - - logger.info("Конфигурация агентов сохранена") - - except Exception as e: - logger.error(f"Ошибка сохранения агентов: {e}") - - def generate_agent_id(self, hostname: str, ip_address: str) -> str: - """Генерировать уникальный ID для агента""" - return f"{hostname}-{ip_address.replace('.', '-')}" - - async def add_agent(self, hostname: str, ip_address: str, ssh_user: str = 'root', - ssh_port: int = 22, ssh_key_path: str = None, - ssh_password: str = None) -> tuple[bool, str]: - """Добавить новый агент в кластер""" - try: - agent_id = self.generate_agent_id(hostname, ip_address) - - # Проверяем, что агент еще не добавлен - if agent_id in self.agents: - return False, f"Агент {agent_id} уже существует в кластере" - - # Создаем объект агента - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_port': ssh_port, - 'ssh_user': ssh_user, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password - } - - agent = ServerAgent(agent_id, agent_config) - - # Тестируем соединение - connection_test = await self._test_ssh_connection(agent) - if not connection_test[0]: - return False, f"Не удалось подключиться к серверу: {connection_test[1]}" - - # Добавляем агент - self.agents[agent_id] = agent - agent.status = 'added' - agent.last_check = datetime.now() - - # Сохраняем конфигурацию - await self.save_agents() - - # Записываем в базу данных - await self.storage.add_agent(agent_id, agent.to_dict()) - - logger.info(f"Агент {agent_id} успешно добавлен в кластер") - return True, f"Агент {hostname} ({ip_address}) добавлен в кластер" - - except Exception as e: - logger.error(f"Ошибка добавления агента: {e}") - return False, f"Ошибка добавления агента: {e}" - - async def _test_ssh_connection(self, agent: ServerAgent) -> tuple[bool, str]: - """Тестирование SSH соединения с агентом""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не указан метод аутентификации (ключ или пароль)" - - # Тестовая команда - stdin, stdout, stderr = ssh.exec_command('echo "PyGuardian Connection Test"') - result = stdout.read().decode().strip() - - ssh.close() - - if "PyGuardian Connection Test" in result: - return True, "Соединение установлено успешно" - else: - return False, "Тестовая команда не выполнена" - - except Exception as e: - return False, f"Ошибка SSH соединения: {e}" - - async def deploy_agent(self, agent_id: str, force_reinstall: bool = False) -> tuple[bool, str]: - """Развернуть PyGuardian агент на удаленном сервере""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Подключение SSH - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Проверка, установлен ли уже PyGuardian - if not force_reinstall: - stdin, stdout, stderr = ssh.exec_command('systemctl status pyguardian-agent') - if stdout.channel.recv_exit_status() == 0: - agent.status = 'deployed' - await self.save_agents() - ssh.close() - return True, f"PyGuardian уже установлен на {agent.hostname}" - - # Создание временного скрипта развертывания - temp_script = f'/tmp/pyguardian_deploy_{secrets.token_hex(8)}.sh' - - # Загрузка скрипта на сервер - sftp = ssh.open_sftp() - with sftp.open(temp_script, 'w') as f: - f.write(self.deployment_script) - sftp.chmod(temp_script, 0o755) - sftp.close() - - # Выполнение скрипта развертывания - logger.info(f"Начинаю развертывание на {agent.hostname}...") - - stdin, stdout, stderr = ssh.exec_command(f'bash {temp_script}') - - # Получение вывода - deploy_output = stdout.read().decode() - deploy_errors = stderr.read().decode() - exit_status = stdout.channel.recv_exit_status() - - # Удаление временного скрипта - ssh.exec_command(f'rm -f {temp_script}') - - if exit_status == 0: - agent.status = 'deployed' - agent.last_check = datetime.now() - await self.save_agents() - - # Обновляем базу данных - await self.storage.update_agent_status(agent_id, 'deployed') - - ssh.close() - logger.info(f"PyGuardian успешно развернут на {agent.hostname}") - return True, f"PyGuardian успешно установлен на {agent.hostname}" - else: - ssh.close() - logger.error(f"Ошибка развертывания на {agent.hostname}: {deploy_errors}") - return False, f"Ошибка установки: {deploy_errors[:500]}" - - except Exception as e: - logger.error(f"Ошибка развертывания агента {agent_id}: {e}") - return False, f"Ошибка развертывания: {e}" - - async def remove_agent(self, agent_id: str, cleanup_remote: bool = False) -> tuple[bool, str]: - """Удалить агент из кластера""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Удаление с удаленного сервера - if cleanup_remote: - cleanup_result = await self._cleanup_remote_agent(agent) - if not cleanup_result[0]: - logger.warning(f"Не удалось очистить удаленный агент: {cleanup_result[1]}") - - # Удаление из локальной конфигурации - del self.agents[agent_id] - await self.save_agents() - - # Удаление из базы данных - await self.storage.remove_agent(agent_id) - - logger.info(f"Агент {agent_id} удален из кластера") - return True, f"Агент {agent.hostname} удален из кластера" - - except Exception as e: - logger.error(f"Ошибка удаления агента: {e}") - return False, f"Ошибка удаления агента: {e}" - - async def _cleanup_remote_agent(self, agent: ServerAgent) -> tuple[bool, str]: - """Очистка PyGuardian на удаленном сервере""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Команды очистки - cleanup_commands = [ - 'systemctl stop pyguardian-agent', - 'systemctl disable pyguardian-agent', - 'rm -f /etc/systemd/system/pyguardian-agent.service', - 'systemctl daemon-reload', - 'rm -rf /opt/pyguardian', - 'rm -rf /var/lib/pyguardian', - 'rm -f /var/log/pyguardian.log' - ] - - for command in cleanup_commands: - ssh.exec_command(command) - - ssh.close() - return True, "Удаленная очистка выполнена" - - except Exception as e: - return False, f"Ошибка очистки: {e}" - - async def check_agent_status(self, agent_id: str) -> tuple[bool, Dict]: - """Проверить статус агента""" - try: - if agent_id not in self.agents: - return False, {"error": f"Агент {agent_id} не найден"} - - agent = self.agents[agent_id] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, {"error": "Не настроена аутентификация"} - - # Проверка статуса сервиса - stdin, stdout, stderr = ssh.exec_command('systemctl is-active pyguardian-agent') - service_status = stdout.read().decode().strip() - - # Проверка версии - stdin, stdout, stderr = ssh.exec_command('cat /opt/pyguardian/VERSION 2>/dev/null || echo "unknown"') - version = stdout.read().decode().strip() - - # Получение системной информации - stdin, stdout, stderr = ssh.exec_command('uptime && df -h / && free -m') - system_info = stdout.read().decode() - - ssh.close() - - # Обновление информации об агенте - agent.status = 'online' if service_status == 'active' else 'offline' - agent.version = version - agent.last_check = datetime.now() - - status_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "service_status": service_status, - "version": version, - "last_check": agent.last_check.isoformat(), - "system_info": system_info - } - - await self.save_agents() - return True, status_info - - except Exception as e: - logger.error(f"Ошибка проверки статуса агента {agent_id}: {e}") - return False, {"error": f"Ошибка проверки статуса: {e}"} - - async def list_agents(self) -> List[Dict]: - """Получить список всех агентов""" - agents_list = [] - for agent_id, agent in self.agents.items(): - agents_list.append({ - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "last_check": agent.last_check.isoformat() if agent.last_check else None, - "version": agent.version - }) - - return agents_list - - async def get_cluster_stats(self) -> Dict: - """Получить статистику кластера""" - total_agents = len(self.agents) - online_agents = len([a for a in self.agents.values() if a.status == 'online']) - offline_agents = len([a for a in self.agents.values() if a.status == 'offline']) - deployed_agents = len([a for a in self.agents.values() if a.status == 'deployed']) - - return { - "cluster_name": self.cluster_name, - "total_agents": total_agents, - "online_agents": online_agents, - "offline_agents": offline_agents, - "deployed_agents": deployed_agents, - "master_server": self.master_server, - "last_updated": datetime.now().isoformat() - } - - async def check_all_agents(self) -> Dict: - """Проверить статус всех агентов""" - results = { - "checked": 0, - "online": 0, - "offline": 0, - "errors": 0, - "details": [] - } - - for agent_id in self.agents.keys(): - try: - success, status_info = await self.check_agent_status(agent_id) - results["checked"] += 1 - - if success: - if status_info.get("status") == "online": - results["online"] += 1 - else: - results["offline"] += 1 - else: - results["errors"] += 1 - - results["details"].append(status_info) - - except Exception as e: - results["errors"] += 1 - results["details"].append({ - "agent_id": agent_id, - "error": str(e) - }) - - return results \ No newline at end of file diff --git a/.history/src/cluster_manager_20251125205700.py b/.history/src/cluster_manager_20251125205700.py deleted file mode 100644 index 544a2f3..0000000 --- a/.history/src/cluster_manager_20251125205700.py +++ /dev/null @@ -1,911 +0,0 @@ -""" -Cluster Manager для PyGuardian -Управление кластером серверов и автоматическое развертывание агентов -""" - -import asyncio -import logging -import json -import subprocess -import os -import yaml -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from pathlib import Path -import aiofiles -import paramiko -from cryptography.fernet import Fernet -import secrets -import string -import hashlib - -# Импортируем систему аутентификации -from .auth import AgentAuthentication, AgentAuthenticationError -from .storage import Storage - -logger = logging.getLogger(__name__) - - -class ServerAgent: - """Представление удаленного сервера-агента""" - - def __init__(self, server_id: str, config: Dict): - self.server_id = server_id - self.hostname = config.get('hostname') - self.ip_address = config.get('ip_address') - self.ssh_port = config.get('ssh_port', 22) - self.ssh_user = config.get('ssh_user', 'root') - self.ssh_key_path = config.get('ssh_key_path') - self.ssh_password = config.get('ssh_password') - self.status = 'unknown' - self.last_check = None - self.version = None - self.stats = {} - - # Новые поля для аутентификации - self.agent_id = config.get('agent_id') - self.secret_key = config.get('secret_key') - self.access_token = config.get('access_token') - self.refresh_token = config.get('refresh_token') - self.token_expires_at = config.get('token_expires_at') - self.last_authenticated = config.get('last_authenticated') - self.is_authenticated = False - - def to_dict(self) -> Dict: - """Конвертация в словарь для сериализации""" - return { - 'server_id': self.server_id, - 'hostname': self.hostname, - 'ip_address': self.ip_address, - 'ssh_port': self.ssh_port, - 'ssh_user': self.ssh_user, - 'ssh_key_path': self.ssh_key_path, - 'status': self.status, - 'last_check': self.last_check.isoformat() if self.last_check else None, - 'version': self.version, - 'agent_id': self.agent_id, - 'is_authenticated': self.is_authenticated, - 'last_authenticated': self.last_authenticated, - 'token_expires_at': self.token_expires_at, - 'stats': self.stats - } - - -class ClusterManager: - """Менеджер кластера серверов""" - - def __init__(self, storage: Storage, config: Dict): - self.storage = storage - self.config = config - - # Параметры кластера - self.cluster_name = config.get('cluster_name', 'PyGuardian-Cluster') - self.master_server = config.get('master_server', True) - self.agents_config_path = config.get('agents_config_path', '/var/lib/pyguardian/agents.yaml') - self.deployment_path = config.get('deployment_path', '/opt/pyguardian') - - # SSH настройки - self.ssh_timeout = config.get('ssh_timeout', 30) - self.ssh_retries = config.get('ssh_retries', 3) - - # Шифрование - self.encryption_key = self._get_or_create_cluster_key() - self.cipher = Fernet(self.encryption_key) - - # Инициализация системы аутентификации - cluster_secret = config.get('cluster_secret', self._generate_cluster_secret()) - self.auth_manager = AgentAuthentication( - secret_key=cluster_secret, - token_expiry_minutes=config.get('token_expiry_minutes', 30) - ) - - # Кэш агентов - self.agents: Dict[str, ServerAgent] = {} - - # Шаблоны для развертывания - self.deployment_script = self._get_deployment_script() - - def _generate_cluster_secret(self) -> str: - """Генерация секрета кластера если он не задан""" - secret = secrets.token_urlsafe(64) - logger.warning(f"Generated new cluster secret. Add to config: cluster_secret: {secret}") - return secret - - def _get_or_create_cluster_key(self) -> bytes: - """Получить или создать ключ шифрования кластера""" - key_file = "/var/lib/pyguardian/cluster_encryption.key" - try: - os.makedirs(os.path.dirname(key_file), exist_ok=True) - - if os.path.exists(key_file): - with open(key_file, 'rb') as f: - return f.read() - else: - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) - logger.info("Создан новый ключ шифрования кластера") - return key - except Exception as e: - logger.error(f"Ошибка работы с ключом кластера: {e}") - return Fernet.generate_key() - - def _get_deployment_script(self) -> str: - """Получить скрипт развертывания агента""" - return '''#!/bin/bash -# PyGuardian Agent Deployment Script -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_NAME="pyguardian-agent" -GITHUB_REPO="https://github.com/your-repo/PyGuardian.git" - -echo "🛡️ Начинаю установку PyGuardian Agent..." - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Скрипт должен быть запущен от имени root" - exit 1 -fi - -# Установка зависимостей -echo "📦 Установка зависимостей..." -if command -v apt >/dev/null 2>&1; then - apt update - apt install -y python3 python3-pip git -elif command -v yum >/dev/null 2>&1; then - yum update -y - yum install -y python3 python3-pip git -elif command -v dnf >/dev/null 2>&1; then - dnf update -y - dnf install -y python3 python3-pip git -else - echo "❌ Неподдерживаемая система. Поддерживаются: Ubuntu/Debian/CentOS/RHEL" - exit 1 -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p $INSTALL_DIR -mkdir -p /var/lib/pyguardian -mkdir -p /var/log/pyguardian - -# Клонирование репозитория -echo "⬇️ Клонирование PyGuardian..." -if [ -d "$INSTALL_DIR/.git" ]; then - cd $INSTALL_DIR && git pull -else - git clone $GITHUB_REPO $INSTALL_DIR -fi - -cd $INSTALL_DIR - -# Установка Python зависимостей -echo "🐍 Установка Python пакетов..." -pip3 install -r requirements.txt - -# Настройка systemd сервиса -echo "⚙️ Настройка systemd сервиса..." -cat > /etc/systemd/system/$SERVICE_NAME.service << EOF -[Unit] -Description=PyGuardian Security Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=$INSTALL_DIR -ExecStart=/usr/bin/python3 $INSTALL_DIR/main.py --agent-mode -Restart=always -RestartSec=10 -Environment=PYTHONPATH=$INSTALL_DIR - -[Install] -WantedBy=multi-user.target -EOF - -# Включение и запуск сервиса -echo "🚀 Запуск PyGuardian Agent..." -systemctl daemon-reload -systemctl enable $SERVICE_NAME -systemctl start $SERVICE_NAME - -echo "✅ PyGuardian Agent успешно установлен и запущен!" -echo "📊 Статус: systemctl status $SERVICE_NAME" -echo "📋 Логи: journalctl -u $SERVICE_NAME -f" -''' - - async def load_agents(self) -> None: - """Загрузить конфигурацию агентов""" - try: - if os.path.exists(self.agents_config_path): - async with aiofiles.open(self.agents_config_path, 'r') as f: - content = await f.read() - agents_config = yaml.safe_load(content) - - self.agents = {} - for agent_id, agent_config in agents_config.get('agents', {}).items(): - self.agents[agent_id] = ServerAgent(agent_id, agent_config) - - logger.info(f"Загружено {len(self.agents)} агентов из конфигурации") - else: - logger.info("Файл конфигурации агентов не найден, создаю новый") - await self.save_agents() - - except Exception as e: - logger.error(f"Ошибка загрузки агентов: {e}") - - async def save_agents(self) -> None: - """Сохранить конфигурацию агентов""" - try: - os.makedirs(os.path.dirname(self.agents_config_path), exist_ok=True) - - agents_config = { - 'cluster': { - 'name': self.cluster_name, - 'master_server': self.master_server, - 'last_updated': datetime.now().isoformat() - }, - 'agents': {} - } - - for agent_id, agent in self.agents.items(): - agents_config['agents'][agent_id] = { - 'hostname': agent.hostname, - 'ip_address': agent.ip_address, - 'ssh_port': agent.ssh_port, - 'ssh_user': agent.ssh_user, - 'ssh_key_path': agent.ssh_key_path, - 'status': agent.status, - 'last_check': agent.last_check.isoformat() if agent.last_check else None, - 'version': agent.version - } - - async with aiofiles.open(self.agents_config_path, 'w') as f: - await f.write(yaml.dump(agents_config, default_flow_style=False)) - - logger.info("Конфигурация агентов сохранена") - - except Exception as e: - logger.error(f"Ошибка сохранения агентов: {e}") - - def generate_agent_id(self, hostname: str, ip_address: str) -> str: - """Генерировать уникальный ID для агента""" - return f"{hostname}-{ip_address.replace('.', '-')}" - - async def add_agent(self, hostname: str, ip_address: str, ssh_user: str = 'root', - ssh_port: int = 22, ssh_key_path: str = None, - ssh_password: str = None) -> tuple[bool, str]: - """Добавить новый агент в кластер""" - try: - agent_id = self.generate_agent_id(hostname, ip_address) - - # Проверяем, что агент еще не добавлен - if agent_id in self.agents: - return False, f"Агент {agent_id} уже существует в кластере" - - # Создаем объект агента - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_port': ssh_port, - 'ssh_user': ssh_user, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password - } - - agent = ServerAgent(agent_id, agent_config) - - # Тестируем соединение - connection_test = await self._test_ssh_connection(agent) - if not connection_test[0]: - return False, f"Не удалось подключиться к серверу: {connection_test[1]}" - - # Добавляем агент - self.agents[agent_id] = agent - agent.status = 'added' - agent.last_check = datetime.now() - - # Сохраняем конфигурацию - await self.save_agents() - - # Записываем в базу данных - await self.storage.add_agent(agent_id, agent.to_dict()) - - logger.info(f"Агент {agent_id} успешно добавлен в кластер") - return True, f"Агент {hostname} ({ip_address}) добавлен в кластер" - - except Exception as e: - logger.error(f"Ошибка добавления агента: {e}") - return False, f"Ошибка добавления агента: {e}" - - async def _test_ssh_connection(self, agent: ServerAgent) -> tuple[bool, str]: - """Тестирование SSH соединения с агентом""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не указан метод аутентификации (ключ или пароль)" - - # Тестовая команда - stdin, stdout, stderr = ssh.exec_command('echo "PyGuardian Connection Test"') - result = stdout.read().decode().strip() - - ssh.close() - - if "PyGuardian Connection Test" in result: - return True, "Соединение установлено успешно" - else: - return False, "Тестовая команда не выполнена" - - except Exception as e: - return False, f"Ошибка SSH соединения: {e}" - - async def deploy_agent(self, agent_id: str, force_reinstall: bool = False) -> tuple[bool, str]: - """Развернуть PyGuardian агент на удаленном сервере""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Подключение SSH - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Проверка, установлен ли уже PyGuardian - if not force_reinstall: - stdin, stdout, stderr = ssh.exec_command('systemctl status pyguardian-agent') - if stdout.channel.recv_exit_status() == 0: - agent.status = 'deployed' - await self.save_agents() - ssh.close() - return True, f"PyGuardian уже установлен на {agent.hostname}" - - # Создание временного скрипта развертывания - temp_script = f'/tmp/pyguardian_deploy_{secrets.token_hex(8)}.sh' - - # Загрузка скрипта на сервер - sftp = ssh.open_sftp() - with sftp.open(temp_script, 'w') as f: - f.write(self.deployment_script) - sftp.chmod(temp_script, 0o755) - sftp.close() - - # Выполнение скрипта развертывания - logger.info(f"Начинаю развертывание на {agent.hostname}...") - - stdin, stdout, stderr = ssh.exec_command(f'bash {temp_script}') - - # Получение вывода - deploy_output = stdout.read().decode() - deploy_errors = stderr.read().decode() - exit_status = stdout.channel.recv_exit_status() - - # Удаление временного скрипта - ssh.exec_command(f'rm -f {temp_script}') - - if exit_status == 0: - agent.status = 'deployed' - agent.last_check = datetime.now() - await self.save_agents() - - # Обновляем базу данных - await self.storage.update_agent_status(agent_id, 'deployed') - - ssh.close() - logger.info(f"PyGuardian успешно развернут на {agent.hostname}") - return True, f"PyGuardian успешно установлен на {agent.hostname}" - else: - ssh.close() - logger.error(f"Ошибка развертывания на {agent.hostname}: {deploy_errors}") - return False, f"Ошибка установки: {deploy_errors[:500]}" - - except Exception as e: - logger.error(f"Ошибка развертывания агента {agent_id}: {e}") - return False, f"Ошибка развертывания: {e}" - - async def remove_agent(self, agent_id: str, cleanup_remote: bool = False) -> tuple[bool, str]: - """Удалить агент из кластера""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Удаление с удаленного сервера - if cleanup_remote: - cleanup_result = await self._cleanup_remote_agent(agent) - if not cleanup_result[0]: - logger.warning(f"Не удалось очистить удаленный агент: {cleanup_result[1]}") - - # Удаление из локальной конфигурации - del self.agents[agent_id] - await self.save_agents() - - # Удаление из базы данных - await self.storage.remove_agent(agent_id) - - logger.info(f"Агент {agent_id} удален из кластера") - return True, f"Агент {agent.hostname} удален из кластера" - - except Exception as e: - logger.error(f"Ошибка удаления агента: {e}") - return False, f"Ошибка удаления агента: {e}" - - async def _cleanup_remote_agent(self, agent: ServerAgent) -> tuple[bool, str]: - """Очистка PyGuardian на удаленном сервере""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Команды очистки - cleanup_commands = [ - 'systemctl stop pyguardian-agent', - 'systemctl disable pyguardian-agent', - 'rm -f /etc/systemd/system/pyguardian-agent.service', - 'systemctl daemon-reload', - 'rm -rf /opt/pyguardian', - 'rm -rf /var/lib/pyguardian', - 'rm -f /var/log/pyguardian.log' - ] - - for command in cleanup_commands: - ssh.exec_command(command) - - ssh.close() - return True, "Удаленная очистка выполнена" - - except Exception as e: - return False, f"Ошибка очистки: {e}" - - async def check_agent_status(self, agent_id: str) -> tuple[bool, Dict]: - """Проверить статус агента""" - try: - if agent_id not in self.agents: - return False, {"error": f"Агент {agent_id} не найден"} - - agent = self.agents[agent_id] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, {"error": "Не настроена аутентификация"} - - # Проверка статуса сервиса - stdin, stdout, stderr = ssh.exec_command('systemctl is-active pyguardian-agent') - service_status = stdout.read().decode().strip() - - # Проверка версии - stdin, stdout, stderr = ssh.exec_command('cat /opt/pyguardian/VERSION 2>/dev/null || echo "unknown"') - version = stdout.read().decode().strip() - - # Получение системной информации - stdin, stdout, stderr = ssh.exec_command('uptime && df -h / && free -m') - system_info = stdout.read().decode() - - ssh.close() - - # Обновление информации об агенте - agent.status = 'online' if service_status == 'active' else 'offline' - agent.version = version - agent.last_check = datetime.now() - - status_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "service_status": service_status, - "version": version, - "last_check": agent.last_check.isoformat(), - "system_info": system_info - } - - await self.save_agents() - return True, status_info - - except Exception as e: - logger.error(f"Ошибка проверки статуса агента {agent_id}: {e}") - return False, {"error": f"Ошибка проверки статуса: {e}"} - - async def list_agents(self) -> List[Dict]: - """Получить список всех агентов""" - agents_list = [] - for agent_id, agent in self.agents.items(): - agents_list.append({ - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "last_check": agent.last_check.isoformat() if agent.last_check else None, - "version": agent.version - }) - - return agents_list - - async def get_cluster_stats(self) -> Dict: - """Получить статистику кластера""" - total_agents = len(self.agents) - online_agents = len([a for a in self.agents.values() if a.status == 'online']) - offline_agents = len([a for a in self.agents.values() if a.status == 'offline']) - deployed_agents = len([a for a in self.agents.values() if a.status == 'deployed']) - - return { - "cluster_name": self.cluster_name, - "total_agents": total_agents, - "online_agents": online_agents, - "offline_agents": offline_agents, - "deployed_agents": deployed_agents, - "master_server": self.master_server, - "last_updated": datetime.now().isoformat() - } - - async def check_all_agents(self) -> Dict: - """Проверить статус всех агентов""" - results = { - "checked": 0, - "online": 0, - "offline": 0, - "errors": 0, - "details": [] - } - - for agent_id in self.agents.keys(): - try: - success, status_info = await self.check_agent_status(agent_id) - results["checked"] += 1 - - if success: - if status_info.get("status") == "online": - results["online"] += 1 - else: - results["offline"] += 1 - else: - results["errors"] += 1 - - results["details"].append(status_info) - - except Exception as e: - results["errors"] += 1 - results["details"].append({ - "agent_id": agent_id, - "error": str(e) - }) - - return results - - # ========================= - # Методы аутентификации агентов - # ========================= - - async def register_new_agent(self, hostname: str, ip_address: str, - ssh_user: str = "root", ssh_port: int = 22, - ssh_key_path: Optional[str] = None, - ssh_password: Optional[str] = None) -> tuple[bool, Dict[str, Any]]: - """Регистрация нового агента с генерацией аутентификационных данных""" - try: - # Создание агентских учетных данных - credentials = self.auth_manager.create_agent_credentials() - - agent_id = credentials['agent_id'] - - # Сохранение аутентификационных данных в базу - success = await self.storage.create_agent_auth( - agent_id=agent_id, - secret_key_hash=credentials['hashed_key'], - salt=credentials['salt'] - ) - - if not success: - return False, {"error": "Failed to store authentication data"} - - # Добавление агента в кластер - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_user': ssh_user, - 'ssh_port': ssh_port, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password, - 'agent_id': agent_id, - 'secret_key': credentials['secret_key'], - 'access_token': credentials['access_token'], - 'refresh_token': credentials['refresh_token'] - } - - await self.add_agent(agent_id, agent_config) - - # Добавление агента в базу данных - await self.storage.register_agent( - agent_id=agent_id, - hostname=hostname, - ip_address=ip_address, - ssh_port=ssh_port, - ssh_user=ssh_user, - status='registered' - ) - - logger.info(f"Successfully registered new agent {agent_id} for {hostname}") - - return True, { - "agent_id": agent_id, - "hostname": hostname, - "ip_address": ip_address, - "secret_key": credentials['secret_key'], # Возвращаем для передачи агенту - "access_token": credentials['access_token'], - "refresh_token": credentials['refresh_token'], - "status": "registered" - } - - except Exception as e: - logger.error(f"Failed to register agent for {hostname}: {e}") - return False, {"error": str(e)} - - async def authenticate_agent(self, agent_id: str, secret_key: str, - ip_address: str) -> tuple[bool, Dict[str, Any]]: - """Аутентификация агента и выдача токенов""" - try: - # Получение сохраненных аутентификационных данных - auth_data = await self.storage.get_agent_auth(agent_id) - - if not auth_data: - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, 'Agent not found' - ) - return False, {"error": "Agent not found"} - - if not auth_data['is_active']: - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, 'Agent deactivated' - ) - return False, {"error": "Agent deactivated"} - - # Аутентификация агента - try: - result = await self.auth_manager.authenticate_agent( - agent_id=agent_id, - secret_key=secret_key, - stored_hash=auth_data['secret_key_hash'], - salt=auth_data['salt'] - ) - - # Обновление времени последней аутентификации - await self.storage.update_agent_last_auth(agent_id) - - # Сохранение токенов в базу данных - token_hash = hashlib.sha256(result['access_token'].encode()).hexdigest() - expires_at = datetime.now() + timedelta(minutes=self.auth_manager.token_expiry) - - await self.storage.store_agent_token( - agent_id=agent_id, - token_hash=token_hash, - token_type='access', - expires_at=expires_at - ) - - # Логирование успешной аутентификации - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', True - ) - - # Обновление статуса агента в кластере - if agent_id in self.agents: - self.agents[agent_id].is_authenticated = True - self.agents[agent_id].last_authenticated = datetime.now().isoformat() - self.agents[agent_id].access_token = result['access_token'] - self.agents[agent_id].token_expires_at = expires_at.isoformat() - - logger.info(f"Successfully authenticated agent {agent_id} from {ip_address}") - - return True, result - - except AgentAuthenticationError as e: - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, str(e) - ) - return False, {"error": str(e)} - - except Exception as e: - logger.error(f"Authentication error for agent {agent_id}: {e}") - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, f"Internal error: {str(e)}" - ) - return False, {"error": "Internal authentication error"} - - async def verify_agent_token(self, token: str, agent_id: Optional[str] = None) -> tuple[bool, str]: - """Проверка токена агента""" - try: - # Верификация JWT токена - agent_from_token = self.auth_manager.validate_agent_request(token, agent_id) - - # Проверка токена в базе данных - token_hash = hashlib.sha256(token.encode()).hexdigest() - is_valid = await self.storage.verify_agent_token(agent_from_token, token_hash) - - if is_valid: - return True, agent_from_token - else: - return False, "Token not found or expired" - - except Exception as e: - logger.error(f"Token verification error: {e}") - return False, str(e) - - async def refresh_agent_token(self, refresh_token: str) -> tuple[bool, Dict[str, str]]: - """Обновление access токена агента""" - try: - new_access_token = self.auth_manager.refresh_access_token(refresh_token) - - # TODO: Получить agent_id из refresh_token для обновления в базе - # payload = jwt.decode(refresh_token, verify=False) - # agent_id = payload.get('agent_id') - - return True, { - "access_token": new_access_token, - "token_type": "Bearer", - "expires_in": self.auth_manager.token_expiry * 60 - } - - except Exception as e: - logger.error(f"Token refresh error: {e}") - return False, {"error": str(e)} - - async def revoke_agent_access(self, agent_id: str) -> bool: - """Отзыв доступа агента (деактивация токенов)""" - try: - # Отзыв всех токенов агента - await self.storage.revoke_agent_tokens(agent_id) - - # Обновление статуса в кэше - if agent_id in self.agents: - self.agents[agent_id].is_authenticated = False - self.agents[agent_id].access_token = None - self.agents[agent_id].refresh_token = None - - # Логирование события - await self.storage.log_agent_auth_event( - agent_id, "system", "revoke_access", True - ) - - logger.info(f"Revoked access for agent {agent_id}") - return True - - except Exception as e: - logger.error(f"Failed to revoke access for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 50) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - return await self.storage.get_agent_auth_logs(agent_id, limit) - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - return await self.storage.get_active_agent_sessions(agent_id) - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов и сессий""" - try: - expired_tokens = await self.storage.cleanup_expired_tokens() - expired_sessions = await self.storage.cleanup_expired_sessions() - - total_cleaned = expired_tokens + expired_sessions - if total_cleaned > 0: - logger.info(f"Cleaned up {expired_tokens} tokens and {expired_sessions} sessions") - - return total_cleaned - except Exception as e: - logger.error(f"Cleanup error: {e}") - return 0 - - async def get_cluster_auth_status(self) -> Dict[str, Any]: - """Получить статус аутентификации всех агентов кластера""" - auth_status = { - "total_agents": len(self.agents), - "authenticated_agents": 0, - "unauthenticated_agents": 0, - "agents": [] - } - - for agent_id, agent in self.agents.items(): - agent_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "is_authenticated": agent.is_authenticated, - "last_authenticated": agent.last_authenticated - } - - if agent.is_authenticated: - auth_status["authenticated_agents"] += 1 - else: - auth_status["unauthenticated_agents"] += 1 - - auth_status["agents"].append(agent_info) - - return auth_status \ No newline at end of file diff --git a/.history/src/cluster_manager_20251125210433.py b/.history/src/cluster_manager_20251125210433.py deleted file mode 100644 index 544a2f3..0000000 --- a/.history/src/cluster_manager_20251125210433.py +++ /dev/null @@ -1,911 +0,0 @@ -""" -Cluster Manager для PyGuardian -Управление кластером серверов и автоматическое развертывание агентов -""" - -import asyncio -import logging -import json -import subprocess -import os -import yaml -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Any -from pathlib import Path -import aiofiles -import paramiko -from cryptography.fernet import Fernet -import secrets -import string -import hashlib - -# Импортируем систему аутентификации -from .auth import AgentAuthentication, AgentAuthenticationError -from .storage import Storage - -logger = logging.getLogger(__name__) - - -class ServerAgent: - """Представление удаленного сервера-агента""" - - def __init__(self, server_id: str, config: Dict): - self.server_id = server_id - self.hostname = config.get('hostname') - self.ip_address = config.get('ip_address') - self.ssh_port = config.get('ssh_port', 22) - self.ssh_user = config.get('ssh_user', 'root') - self.ssh_key_path = config.get('ssh_key_path') - self.ssh_password = config.get('ssh_password') - self.status = 'unknown' - self.last_check = None - self.version = None - self.stats = {} - - # Новые поля для аутентификации - self.agent_id = config.get('agent_id') - self.secret_key = config.get('secret_key') - self.access_token = config.get('access_token') - self.refresh_token = config.get('refresh_token') - self.token_expires_at = config.get('token_expires_at') - self.last_authenticated = config.get('last_authenticated') - self.is_authenticated = False - - def to_dict(self) -> Dict: - """Конвертация в словарь для сериализации""" - return { - 'server_id': self.server_id, - 'hostname': self.hostname, - 'ip_address': self.ip_address, - 'ssh_port': self.ssh_port, - 'ssh_user': self.ssh_user, - 'ssh_key_path': self.ssh_key_path, - 'status': self.status, - 'last_check': self.last_check.isoformat() if self.last_check else None, - 'version': self.version, - 'agent_id': self.agent_id, - 'is_authenticated': self.is_authenticated, - 'last_authenticated': self.last_authenticated, - 'token_expires_at': self.token_expires_at, - 'stats': self.stats - } - - -class ClusterManager: - """Менеджер кластера серверов""" - - def __init__(self, storage: Storage, config: Dict): - self.storage = storage - self.config = config - - # Параметры кластера - self.cluster_name = config.get('cluster_name', 'PyGuardian-Cluster') - self.master_server = config.get('master_server', True) - self.agents_config_path = config.get('agents_config_path', '/var/lib/pyguardian/agents.yaml') - self.deployment_path = config.get('deployment_path', '/opt/pyguardian') - - # SSH настройки - self.ssh_timeout = config.get('ssh_timeout', 30) - self.ssh_retries = config.get('ssh_retries', 3) - - # Шифрование - self.encryption_key = self._get_or_create_cluster_key() - self.cipher = Fernet(self.encryption_key) - - # Инициализация системы аутентификации - cluster_secret = config.get('cluster_secret', self._generate_cluster_secret()) - self.auth_manager = AgentAuthentication( - secret_key=cluster_secret, - token_expiry_minutes=config.get('token_expiry_minutes', 30) - ) - - # Кэш агентов - self.agents: Dict[str, ServerAgent] = {} - - # Шаблоны для развертывания - self.deployment_script = self._get_deployment_script() - - def _generate_cluster_secret(self) -> str: - """Генерация секрета кластера если он не задан""" - secret = secrets.token_urlsafe(64) - logger.warning(f"Generated new cluster secret. Add to config: cluster_secret: {secret}") - return secret - - def _get_or_create_cluster_key(self) -> bytes: - """Получить или создать ключ шифрования кластера""" - key_file = "/var/lib/pyguardian/cluster_encryption.key" - try: - os.makedirs(os.path.dirname(key_file), exist_ok=True) - - if os.path.exists(key_file): - with open(key_file, 'rb') as f: - return f.read() - else: - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) - logger.info("Создан новый ключ шифрования кластера") - return key - except Exception as e: - logger.error(f"Ошибка работы с ключом кластера: {e}") - return Fernet.generate_key() - - def _get_deployment_script(self) -> str: - """Получить скрипт развертывания агента""" - return '''#!/bin/bash -# PyGuardian Agent Deployment Script -set -e - -INSTALL_DIR="/opt/pyguardian" -SERVICE_NAME="pyguardian-agent" -GITHUB_REPO="https://github.com/your-repo/PyGuardian.git" - -echo "🛡️ Начинаю установку PyGuardian Agent..." - -# Проверка прав root -if [[ $EUID -ne 0 ]]; then - echo "❌ Скрипт должен быть запущен от имени root" - exit 1 -fi - -# Установка зависимостей -echo "📦 Установка зависимостей..." -if command -v apt >/dev/null 2>&1; then - apt update - apt install -y python3 python3-pip git -elif command -v yum >/dev/null 2>&1; then - yum update -y - yum install -y python3 python3-pip git -elif command -v dnf >/dev/null 2>&1; then - dnf update -y - dnf install -y python3 python3-pip git -else - echo "❌ Неподдерживаемая система. Поддерживаются: Ubuntu/Debian/CentOS/RHEL" - exit 1 -fi - -# Создание директорий -echo "📁 Создание директорий..." -mkdir -p $INSTALL_DIR -mkdir -p /var/lib/pyguardian -mkdir -p /var/log/pyguardian - -# Клонирование репозитория -echo "⬇️ Клонирование PyGuardian..." -if [ -d "$INSTALL_DIR/.git" ]; then - cd $INSTALL_DIR && git pull -else - git clone $GITHUB_REPO $INSTALL_DIR -fi - -cd $INSTALL_DIR - -# Установка Python зависимостей -echo "🐍 Установка Python пакетов..." -pip3 install -r requirements.txt - -# Настройка systemd сервиса -echo "⚙️ Настройка systemd сервиса..." -cat > /etc/systemd/system/$SERVICE_NAME.service << EOF -[Unit] -Description=PyGuardian Security Agent -After=network.target -Wants=network.target - -[Service] -Type=simple -User=root -WorkingDirectory=$INSTALL_DIR -ExecStart=/usr/bin/python3 $INSTALL_DIR/main.py --agent-mode -Restart=always -RestartSec=10 -Environment=PYTHONPATH=$INSTALL_DIR - -[Install] -WantedBy=multi-user.target -EOF - -# Включение и запуск сервиса -echo "🚀 Запуск PyGuardian Agent..." -systemctl daemon-reload -systemctl enable $SERVICE_NAME -systemctl start $SERVICE_NAME - -echo "✅ PyGuardian Agent успешно установлен и запущен!" -echo "📊 Статус: systemctl status $SERVICE_NAME" -echo "📋 Логи: journalctl -u $SERVICE_NAME -f" -''' - - async def load_agents(self) -> None: - """Загрузить конфигурацию агентов""" - try: - if os.path.exists(self.agents_config_path): - async with aiofiles.open(self.agents_config_path, 'r') as f: - content = await f.read() - agents_config = yaml.safe_load(content) - - self.agents = {} - for agent_id, agent_config in agents_config.get('agents', {}).items(): - self.agents[agent_id] = ServerAgent(agent_id, agent_config) - - logger.info(f"Загружено {len(self.agents)} агентов из конфигурации") - else: - logger.info("Файл конфигурации агентов не найден, создаю новый") - await self.save_agents() - - except Exception as e: - logger.error(f"Ошибка загрузки агентов: {e}") - - async def save_agents(self) -> None: - """Сохранить конфигурацию агентов""" - try: - os.makedirs(os.path.dirname(self.agents_config_path), exist_ok=True) - - agents_config = { - 'cluster': { - 'name': self.cluster_name, - 'master_server': self.master_server, - 'last_updated': datetime.now().isoformat() - }, - 'agents': {} - } - - for agent_id, agent in self.agents.items(): - agents_config['agents'][agent_id] = { - 'hostname': agent.hostname, - 'ip_address': agent.ip_address, - 'ssh_port': agent.ssh_port, - 'ssh_user': agent.ssh_user, - 'ssh_key_path': agent.ssh_key_path, - 'status': agent.status, - 'last_check': agent.last_check.isoformat() if agent.last_check else None, - 'version': agent.version - } - - async with aiofiles.open(self.agents_config_path, 'w') as f: - await f.write(yaml.dump(agents_config, default_flow_style=False)) - - logger.info("Конфигурация агентов сохранена") - - except Exception as e: - logger.error(f"Ошибка сохранения агентов: {e}") - - def generate_agent_id(self, hostname: str, ip_address: str) -> str: - """Генерировать уникальный ID для агента""" - return f"{hostname}-{ip_address.replace('.', '-')}" - - async def add_agent(self, hostname: str, ip_address: str, ssh_user: str = 'root', - ssh_port: int = 22, ssh_key_path: str = None, - ssh_password: str = None) -> tuple[bool, str]: - """Добавить новый агент в кластер""" - try: - agent_id = self.generate_agent_id(hostname, ip_address) - - # Проверяем, что агент еще не добавлен - if agent_id in self.agents: - return False, f"Агент {agent_id} уже существует в кластере" - - # Создаем объект агента - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_port': ssh_port, - 'ssh_user': ssh_user, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password - } - - agent = ServerAgent(agent_id, agent_config) - - # Тестируем соединение - connection_test = await self._test_ssh_connection(agent) - if not connection_test[0]: - return False, f"Не удалось подключиться к серверу: {connection_test[1]}" - - # Добавляем агент - self.agents[agent_id] = agent - agent.status = 'added' - agent.last_check = datetime.now() - - # Сохраняем конфигурацию - await self.save_agents() - - # Записываем в базу данных - await self.storage.add_agent(agent_id, agent.to_dict()) - - logger.info(f"Агент {agent_id} успешно добавлен в кластер") - return True, f"Агент {hostname} ({ip_address}) добавлен в кластер" - - except Exception as e: - logger.error(f"Ошибка добавления агента: {e}") - return False, f"Ошибка добавления агента: {e}" - - async def _test_ssh_connection(self, agent: ServerAgent) -> tuple[bool, str]: - """Тестирование SSH соединения с агентом""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не указан метод аутентификации (ключ или пароль)" - - # Тестовая команда - stdin, stdout, stderr = ssh.exec_command('echo "PyGuardian Connection Test"') - result = stdout.read().decode().strip() - - ssh.close() - - if "PyGuardian Connection Test" in result: - return True, "Соединение установлено успешно" - else: - return False, "Тестовая команда не выполнена" - - except Exception as e: - return False, f"Ошибка SSH соединения: {e}" - - async def deploy_agent(self, agent_id: str, force_reinstall: bool = False) -> tuple[bool, str]: - """Развернуть PyGuardian агент на удаленном сервере""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Подключение SSH - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Проверка, установлен ли уже PyGuardian - if not force_reinstall: - stdin, stdout, stderr = ssh.exec_command('systemctl status pyguardian-agent') - if stdout.channel.recv_exit_status() == 0: - agent.status = 'deployed' - await self.save_agents() - ssh.close() - return True, f"PyGuardian уже установлен на {agent.hostname}" - - # Создание временного скрипта развертывания - temp_script = f'/tmp/pyguardian_deploy_{secrets.token_hex(8)}.sh' - - # Загрузка скрипта на сервер - sftp = ssh.open_sftp() - with sftp.open(temp_script, 'w') as f: - f.write(self.deployment_script) - sftp.chmod(temp_script, 0o755) - sftp.close() - - # Выполнение скрипта развертывания - logger.info(f"Начинаю развертывание на {agent.hostname}...") - - stdin, stdout, stderr = ssh.exec_command(f'bash {temp_script}') - - # Получение вывода - deploy_output = stdout.read().decode() - deploy_errors = stderr.read().decode() - exit_status = stdout.channel.recv_exit_status() - - # Удаление временного скрипта - ssh.exec_command(f'rm -f {temp_script}') - - if exit_status == 0: - agent.status = 'deployed' - agent.last_check = datetime.now() - await self.save_agents() - - # Обновляем базу данных - await self.storage.update_agent_status(agent_id, 'deployed') - - ssh.close() - logger.info(f"PyGuardian успешно развернут на {agent.hostname}") - return True, f"PyGuardian успешно установлен на {agent.hostname}" - else: - ssh.close() - logger.error(f"Ошибка развертывания на {agent.hostname}: {deploy_errors}") - return False, f"Ошибка установки: {deploy_errors[:500]}" - - except Exception as e: - logger.error(f"Ошибка развертывания агента {agent_id}: {e}") - return False, f"Ошибка развертывания: {e}" - - async def remove_agent(self, agent_id: str, cleanup_remote: bool = False) -> tuple[bool, str]: - """Удалить агент из кластера""" - try: - if agent_id not in self.agents: - return False, f"Агент {agent_id} не найден" - - agent = self.agents[agent_id] - - # Удаление с удаленного сервера - if cleanup_remote: - cleanup_result = await self._cleanup_remote_agent(agent) - if not cleanup_result[0]: - logger.warning(f"Не удалось очистить удаленный агент: {cleanup_result[1]}") - - # Удаление из локальной конфигурации - del self.agents[agent_id] - await self.save_agents() - - # Удаление из базы данных - await self.storage.remove_agent(agent_id) - - logger.info(f"Агент {agent_id} удален из кластера") - return True, f"Агент {agent.hostname} удален из кластера" - - except Exception as e: - logger.error(f"Ошибка удаления агента: {e}") - return False, f"Ошибка удаления агента: {e}" - - async def _cleanup_remote_agent(self, agent: ServerAgent) -> tuple[bool, str]: - """Очистка PyGuardian на удаленном сервере""" - try: - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, "Не настроена аутентификация" - - # Команды очистки - cleanup_commands = [ - 'systemctl stop pyguardian-agent', - 'systemctl disable pyguardian-agent', - 'rm -f /etc/systemd/system/pyguardian-agent.service', - 'systemctl daemon-reload', - 'rm -rf /opt/pyguardian', - 'rm -rf /var/lib/pyguardian', - 'rm -f /var/log/pyguardian.log' - ] - - for command in cleanup_commands: - ssh.exec_command(command) - - ssh.close() - return True, "Удаленная очистка выполнена" - - except Exception as e: - return False, f"Ошибка очистки: {e}" - - async def check_agent_status(self, agent_id: str) -> tuple[bool, Dict]: - """Проверить статус агента""" - try: - if agent_id not in self.agents: - return False, {"error": f"Агент {agent_id} не найден"} - - agent = self.agents[agent_id] - - ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - - # Подключение - if agent.ssh_key_path and os.path.exists(agent.ssh_key_path): - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - key_filename=agent.ssh_key_path, - timeout=self.ssh_timeout - ) - elif agent.ssh_password: - ssh.connect( - hostname=agent.ip_address, - port=agent.ssh_port, - username=agent.ssh_user, - password=agent.ssh_password, - timeout=self.ssh_timeout - ) - else: - return False, {"error": "Не настроена аутентификация"} - - # Проверка статуса сервиса - stdin, stdout, stderr = ssh.exec_command('systemctl is-active pyguardian-agent') - service_status = stdout.read().decode().strip() - - # Проверка версии - stdin, stdout, stderr = ssh.exec_command('cat /opt/pyguardian/VERSION 2>/dev/null || echo "unknown"') - version = stdout.read().decode().strip() - - # Получение системной информации - stdin, stdout, stderr = ssh.exec_command('uptime && df -h / && free -m') - system_info = stdout.read().decode() - - ssh.close() - - # Обновление информации об агенте - agent.status = 'online' if service_status == 'active' else 'offline' - agent.version = version - agent.last_check = datetime.now() - - status_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "service_status": service_status, - "version": version, - "last_check": agent.last_check.isoformat(), - "system_info": system_info - } - - await self.save_agents() - return True, status_info - - except Exception as e: - logger.error(f"Ошибка проверки статуса агента {agent_id}: {e}") - return False, {"error": f"Ошибка проверки статуса: {e}"} - - async def list_agents(self) -> List[Dict]: - """Получить список всех агентов""" - agents_list = [] - for agent_id, agent in self.agents.items(): - agents_list.append({ - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "status": agent.status, - "last_check": agent.last_check.isoformat() if agent.last_check else None, - "version": agent.version - }) - - return agents_list - - async def get_cluster_stats(self) -> Dict: - """Получить статистику кластера""" - total_agents = len(self.agents) - online_agents = len([a for a in self.agents.values() if a.status == 'online']) - offline_agents = len([a for a in self.agents.values() if a.status == 'offline']) - deployed_agents = len([a for a in self.agents.values() if a.status == 'deployed']) - - return { - "cluster_name": self.cluster_name, - "total_agents": total_agents, - "online_agents": online_agents, - "offline_agents": offline_agents, - "deployed_agents": deployed_agents, - "master_server": self.master_server, - "last_updated": datetime.now().isoformat() - } - - async def check_all_agents(self) -> Dict: - """Проверить статус всех агентов""" - results = { - "checked": 0, - "online": 0, - "offline": 0, - "errors": 0, - "details": [] - } - - for agent_id in self.agents.keys(): - try: - success, status_info = await self.check_agent_status(agent_id) - results["checked"] += 1 - - if success: - if status_info.get("status") == "online": - results["online"] += 1 - else: - results["offline"] += 1 - else: - results["errors"] += 1 - - results["details"].append(status_info) - - except Exception as e: - results["errors"] += 1 - results["details"].append({ - "agent_id": agent_id, - "error": str(e) - }) - - return results - - # ========================= - # Методы аутентификации агентов - # ========================= - - async def register_new_agent(self, hostname: str, ip_address: str, - ssh_user: str = "root", ssh_port: int = 22, - ssh_key_path: Optional[str] = None, - ssh_password: Optional[str] = None) -> tuple[bool, Dict[str, Any]]: - """Регистрация нового агента с генерацией аутентификационных данных""" - try: - # Создание агентских учетных данных - credentials = self.auth_manager.create_agent_credentials() - - agent_id = credentials['agent_id'] - - # Сохранение аутентификационных данных в базу - success = await self.storage.create_agent_auth( - agent_id=agent_id, - secret_key_hash=credentials['hashed_key'], - salt=credentials['salt'] - ) - - if not success: - return False, {"error": "Failed to store authentication data"} - - # Добавление агента в кластер - agent_config = { - 'hostname': hostname, - 'ip_address': ip_address, - 'ssh_user': ssh_user, - 'ssh_port': ssh_port, - 'ssh_key_path': ssh_key_path, - 'ssh_password': ssh_password, - 'agent_id': agent_id, - 'secret_key': credentials['secret_key'], - 'access_token': credentials['access_token'], - 'refresh_token': credentials['refresh_token'] - } - - await self.add_agent(agent_id, agent_config) - - # Добавление агента в базу данных - await self.storage.register_agent( - agent_id=agent_id, - hostname=hostname, - ip_address=ip_address, - ssh_port=ssh_port, - ssh_user=ssh_user, - status='registered' - ) - - logger.info(f"Successfully registered new agent {agent_id} for {hostname}") - - return True, { - "agent_id": agent_id, - "hostname": hostname, - "ip_address": ip_address, - "secret_key": credentials['secret_key'], # Возвращаем для передачи агенту - "access_token": credentials['access_token'], - "refresh_token": credentials['refresh_token'], - "status": "registered" - } - - except Exception as e: - logger.error(f"Failed to register agent for {hostname}: {e}") - return False, {"error": str(e)} - - async def authenticate_agent(self, agent_id: str, secret_key: str, - ip_address: str) -> tuple[bool, Dict[str, Any]]: - """Аутентификация агента и выдача токенов""" - try: - # Получение сохраненных аутентификационных данных - auth_data = await self.storage.get_agent_auth(agent_id) - - if not auth_data: - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, 'Agent not found' - ) - return False, {"error": "Agent not found"} - - if not auth_data['is_active']: - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, 'Agent deactivated' - ) - return False, {"error": "Agent deactivated"} - - # Аутентификация агента - try: - result = await self.auth_manager.authenticate_agent( - agent_id=agent_id, - secret_key=secret_key, - stored_hash=auth_data['secret_key_hash'], - salt=auth_data['salt'] - ) - - # Обновление времени последней аутентификации - await self.storage.update_agent_last_auth(agent_id) - - # Сохранение токенов в базу данных - token_hash = hashlib.sha256(result['access_token'].encode()).hexdigest() - expires_at = datetime.now() + timedelta(minutes=self.auth_manager.token_expiry) - - await self.storage.store_agent_token( - agent_id=agent_id, - token_hash=token_hash, - token_type='access', - expires_at=expires_at - ) - - # Логирование успешной аутентификации - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', True - ) - - # Обновление статуса агента в кластере - if agent_id in self.agents: - self.agents[agent_id].is_authenticated = True - self.agents[agent_id].last_authenticated = datetime.now().isoformat() - self.agents[agent_id].access_token = result['access_token'] - self.agents[agent_id].token_expires_at = expires_at.isoformat() - - logger.info(f"Successfully authenticated agent {agent_id} from {ip_address}") - - return True, result - - except AgentAuthenticationError as e: - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, str(e) - ) - return False, {"error": str(e)} - - except Exception as e: - logger.error(f"Authentication error for agent {agent_id}: {e}") - await self.storage.log_agent_auth_event( - agent_id, ip_address, 'login', False, f"Internal error: {str(e)}" - ) - return False, {"error": "Internal authentication error"} - - async def verify_agent_token(self, token: str, agent_id: Optional[str] = None) -> tuple[bool, str]: - """Проверка токена агента""" - try: - # Верификация JWT токена - agent_from_token = self.auth_manager.validate_agent_request(token, agent_id) - - # Проверка токена в базе данных - token_hash = hashlib.sha256(token.encode()).hexdigest() - is_valid = await self.storage.verify_agent_token(agent_from_token, token_hash) - - if is_valid: - return True, agent_from_token - else: - return False, "Token not found or expired" - - except Exception as e: - logger.error(f"Token verification error: {e}") - return False, str(e) - - async def refresh_agent_token(self, refresh_token: str) -> tuple[bool, Dict[str, str]]: - """Обновление access токена агента""" - try: - new_access_token = self.auth_manager.refresh_access_token(refresh_token) - - # TODO: Получить agent_id из refresh_token для обновления в базе - # payload = jwt.decode(refresh_token, verify=False) - # agent_id = payload.get('agent_id') - - return True, { - "access_token": new_access_token, - "token_type": "Bearer", - "expires_in": self.auth_manager.token_expiry * 60 - } - - except Exception as e: - logger.error(f"Token refresh error: {e}") - return False, {"error": str(e)} - - async def revoke_agent_access(self, agent_id: str) -> bool: - """Отзыв доступа агента (деактивация токенов)""" - try: - # Отзыв всех токенов агента - await self.storage.revoke_agent_tokens(agent_id) - - # Обновление статуса в кэше - if agent_id in self.agents: - self.agents[agent_id].is_authenticated = False - self.agents[agent_id].access_token = None - self.agents[agent_id].refresh_token = None - - # Логирование события - await self.storage.log_agent_auth_event( - agent_id, "system", "revoke_access", True - ) - - logger.info(f"Revoked access for agent {agent_id}") - return True - - except Exception as e: - logger.error(f"Failed to revoke access for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 50) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - return await self.storage.get_agent_auth_logs(agent_id, limit) - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - return await self.storage.get_active_agent_sessions(agent_id) - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов и сессий""" - try: - expired_tokens = await self.storage.cleanup_expired_tokens() - expired_sessions = await self.storage.cleanup_expired_sessions() - - total_cleaned = expired_tokens + expired_sessions - if total_cleaned > 0: - logger.info(f"Cleaned up {expired_tokens} tokens and {expired_sessions} sessions") - - return total_cleaned - except Exception as e: - logger.error(f"Cleanup error: {e}") - return 0 - - async def get_cluster_auth_status(self) -> Dict[str, Any]: - """Получить статус аутентификации всех агентов кластера""" - auth_status = { - "total_agents": len(self.agents), - "authenticated_agents": 0, - "unauthenticated_agents": 0, - "agents": [] - } - - for agent_id, agent in self.agents.items(): - agent_info = { - "agent_id": agent_id, - "hostname": agent.hostname, - "ip_address": agent.ip_address, - "is_authenticated": agent.is_authenticated, - "last_authenticated": agent.last_authenticated - } - - if agent.is_authenticated: - auth_status["authenticated_agents"] += 1 - else: - auth_status["unauthenticated_agents"] += 1 - - auth_status["agents"].append(agent_info) - - return auth_status \ No newline at end of file diff --git a/.history/src/firewall_20251125194516.py b/.history/src/firewall_20251125194516.py deleted file mode 100644 index 0f14308..0000000 --- a/.history/src/firewall_20251125194516.py +++ /dev/null @@ -1,435 +0,0 @@ -""" -Firewall module для PyGuardian -Управление iptables/nftables для блокировки IP-адресов -""" - -import asyncio -import subprocess -import logging -from typing import Dict, List, Optional -from abc import ABC, abstractmethod - -logger = logging.getLogger(__name__) - - -class FirewallInterface(ABC): - """Абстрактный интерфейс для работы с firewall""" - - @abstractmethod - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес""" - pass - - @abstractmethod - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - pass - - @abstractmethod - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - pass - - @abstractmethod - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - pass - - @abstractmethod - async def setup_chains(self) -> bool: - """Настроить цепочки firewall""" - pass - - -class IptablesFirewall(FirewallInterface): - """Реализация для iptables""" - - def __init__(self, config: Dict): - self.chain = config.get('chain', 'INPUT') - self.target = config.get('target', 'DROP') - self.table = config.get('iptables', {}).get('table', 'filter') - self.comment = "PyGuardian-ban" - - async def _run_command(self, command: List[str]) -> tuple[bool, str]: - """Выполнить команду iptables""" - try: - result = await asyncio.create_subprocess_exec( - *command, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await result.communicate() - - if result.returncode == 0: - return True, stdout.decode().strip() - else: - error_msg = stderr.decode().strip() - logger.error(f"Ошибка выполнения команды {' '.join(command)}: {error_msg}") - return False, error_msg - - except Exception as e: - logger.error(f"Исключение при выполнении команды {' '.join(command)}: {e}") - return False, str(e) - - async def setup_chains(self) -> bool: - """Настроить цепочки iptables""" - try: - # Создаем специальную цепочку для PyGuardian если не существует - pyguardian_chain = "PYGUARDIAN" - - # Проверяем, существует ли цепочка - success, _ = await self._run_command([ - "iptables", "-t", self.table, "-L", pyguardian_chain, "-n" - ]) - - if not success: - # Создаем цепочку - success, _ = await self._run_command([ - "iptables", "-t", self.table, "-N", pyguardian_chain - ]) - if not success: - return False - - logger.info(f"Создана цепочка {pyguardian_chain}") - - # Проверяем, есть ли правило перехода в нашу цепочку - success, output = await self._run_command([ - "iptables", "-t", self.table, "-L", self.chain, "-n", "--line-numbers" - ]) - - if success and pyguardian_chain not in output: - # Добавляем правило в начало цепочки INPUT - success, _ = await self._run_command([ - "iptables", "-t", self.table, "-I", self.chain, "1", - "-j", pyguardian_chain - ]) - if success: - logger.info(f"Добавлено правило перехода в цепочку {pyguardian_chain}") - else: - return False - - return True - - except Exception as e: - logger.error(f"Ошибка настройки цепочек iptables: {e}") - return False - - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес через iptables""" - try: - # Проверяем, не забанен ли уже - if await self.is_banned(ip): - logger.warning(f"IP {ip} уже забанен в iptables") - return True - - # Добавляем правило блокировки - command = [ - "iptables", "-t", self.table, "-A", "PYGUARDIAN", - "-s", ip, "-j", self.target, - "-m", "comment", "--comment", self.comment - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} заблокирован в iptables") - return True - else: - logger.error(f"Не удалось заблокировать IP {ip}: {error}") - return False - - except Exception as e: - logger.error(f"Ошибка при блокировке IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - try: - # Находим и удаляем правило - command = [ - "iptables", "-t", self.table, "-D", "PYGUARDIAN", - "-s", ip, "-j", self.target, - "-m", "comment", "--comment", self.comment - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} разблокирован в iptables") - return True - else: - # Возможно, правило уже удалено - logger.warning(f"Не удалось удалить правило для IP {ip}: {error}") - return True - - except Exception as e: - logger.error(f"Ошибка при разблокировке IP {ip}: {e}") - return False - - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - try: - command = [ - "iptables", "-t", self.table, "-L", "PYGUARDIAN", "-n" - ] - - success, output = await self._run_command(command) - if success: - return ip in output and self.comment in output - else: - return False - - except Exception as e: - logger.error(f"Ошибка при проверке IP {ip}: {e}") - return False - - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - try: - command = [ - "iptables", "-t", self.table, "-L", "PYGUARDIAN", "-n" - ] - - success, output = await self._run_command(command) - if not success: - return [] - - banned_ips = [] - for line in output.split('\n'): - if self.comment in line and self.target in line: - parts = line.split() - if len(parts) >= 4: - source_ip = parts[3] - if '/' not in source_ip: # Исключаем сети - banned_ips.append(source_ip) - - return banned_ips - - except Exception as e: - logger.error(f"Ошибка при получении списка забаненных IP: {e}") - return [] - - -class NftablesFirewall(FirewallInterface): - """Реализация для nftables""" - - def __init__(self, config: Dict): - self.table = config.get('nftables', {}).get('table', 'inet pyguardian') - self.chain = config.get('nftables', {}).get('chain', 'input') - self.set_name = "banned_ips" - - async def _run_command(self, command: List[str]) -> tuple[bool, str]: - """Выполнить команду nft""" - try: - result = await asyncio.create_subprocess_exec( - *command, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await result.communicate() - - if result.returncode == 0: - return True, stdout.decode().strip() - else: - error_msg = stderr.decode().strip() - logger.error(f"Ошибка выполнения команды {' '.join(command)}: {error_msg}") - return False, error_msg - - except Exception as e: - logger.error(f"Исключение при выполнении команды {' '.join(command)}: {e}") - return False, str(e) - - async def setup_chains(self) -> bool: - """Настроить таблицу и цепочку nftables""" - try: - # Создаем таблицу - success, _ = await self._run_command([ - "nft", "create", "table", self.table - ]) - # Игнорируем ошибку, если таблица уже существует - - # Создаем set для хранения IP адресов - success, _ = await self._run_command([ - "nft", "create", "set", f"{self.table}", self.set_name, - "{ type ipv4_addr; }" - ]) - # Игнорируем ошибку, если set уже существует - - # Создаем цепочку - success, _ = await self._run_command([ - "nft", "create", "chain", f"{self.table}", self.chain, - "{ type filter hook input priority 0; policy accept; }" - ]) - # Игнорируем ошибку, если цепочка уже существует - - # Добавляем правило блокировки для IP из set - success, _ = await self._run_command([ - "nft", "add", "rule", f"{self.table}", self.chain, - "ip", "saddr", f"@{self.set_name}", "drop" - ]) - - logger.info("Настройка nftables выполнена успешно") - return True - - except Exception as e: - logger.error(f"Ошибка настройки nftables: {e}") - return False - - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес через nftables""" - try: - command = [ - "nft", "add", "element", f"{self.table}", self.set_name, - f"{{ {ip} }}" - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} заблокирован в nftables") - return True - else: - logger.error(f"Не удалось заблокировать IP {ip}: {error}") - return False - - except Exception as e: - logger.error(f"Ошибка при блокировке IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - try: - command = [ - "nft", "delete", "element", f"{self.table}", self.set_name, - f"{{ {ip} }}" - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} разблокирован в nftables") - return True - else: - logger.warning(f"Не удалось удалить IP {ip}: {error}") - return True # Возможно, IP уже не в списке - - except Exception as e: - logger.error(f"Ошибка при разблокировке IP {ip}: {e}") - return False - - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - try: - banned_ips = await self.list_banned_ips() - return ip in banned_ips - - except Exception as e: - logger.error(f"Ошибка при проверке IP {ip}: {e}") - return False - - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - try: - command = [ - "nft", "list", "set", f"{self.table}", self.set_name - ] - - success, output = await self._run_command(command) - if not success: - return [] - - banned_ips = [] - in_elements = False - - for line in output.split('\n'): - line = line.strip() - if 'elements = {' in line: - in_elements = True - # Проверяем, есть ли IP на той же строке - if '}' in line: - elements_part = line.split('{')[1].split('}')[0] - banned_ips.extend([ip.strip() for ip in elements_part.split(',') if ip.strip()]) - break - elif in_elements: - if '}' in line: - elements_part = line.split('}')[0] - banned_ips.extend([ip.strip() for ip in elements_part.split(',') if ip.strip()]) - break - else: - banned_ips.extend([ip.strip() for ip in line.split(',') if ip.strip()]) - - return [ip for ip in banned_ips if ip and ip != ''] - - except Exception as e: - logger.error(f"Ошибка при получении списка забаненных IP: {e}") - return [] - - -class FirewallManager: - """Менеджер для управления firewall""" - - def __init__(self, config: Dict): - self.config = config - backend = config.get('backend', 'iptables').lower() - - if backend == 'iptables': - self.firewall = IptablesFirewall(config) - elif backend == 'nftables': - self.firewall = NftablesFirewall(config) - else: - raise ValueError(f"Неподдерживаемый backend: {backend}") - - self.backend = backend - logger.info(f"Инициализирован {backend} firewall") - - async def setup(self) -> bool: - """Настроить firewall""" - return await self.firewall.setup_chains() - - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес""" - return await self.firewall.ban_ip(ip) - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - return await self.firewall.unban_ip(ip) - - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - return await self.firewall.is_banned(ip) - - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - return await self.firewall.list_banned_ips() - - async def get_status(self) -> Dict: - """Получить статус firewall""" - try: - banned_ips = await self.list_banned_ips() - return { - 'backend': self.backend, - 'active': True, - 'banned_count': len(banned_ips), - 'banned_ips': banned_ips[:10] # Первые 10 для отображения - } - except Exception as e: - logger.error(f"Ошибка получения статуса firewall: {e}") - return { - 'backend': self.backend, - 'active': False, - 'error': str(e) - } - - async def cleanup_expired_bans(self, valid_ips: List[str]) -> int: - """Очистить firewall от IP, которые больше не должны быть забанены""" - try: - current_banned = await self.list_banned_ips() - removed_count = 0 - - for ip in current_banned: - if ip not in valid_ips: - if await self.unban_ip(ip): - removed_count += 1 - logger.info(f"Удален устаревший бан для IP {ip}") - - return removed_count - - except Exception as e: - logger.error(f"Ошибка очистки устаревших банов: {e}") - return 0 \ No newline at end of file diff --git a/.history/src/firewall_20251125202055.py b/.history/src/firewall_20251125202055.py deleted file mode 100644 index 0f14308..0000000 --- a/.history/src/firewall_20251125202055.py +++ /dev/null @@ -1,435 +0,0 @@ -""" -Firewall module для PyGuardian -Управление iptables/nftables для блокировки IP-адресов -""" - -import asyncio -import subprocess -import logging -from typing import Dict, List, Optional -from abc import ABC, abstractmethod - -logger = logging.getLogger(__name__) - - -class FirewallInterface(ABC): - """Абстрактный интерфейс для работы с firewall""" - - @abstractmethod - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес""" - pass - - @abstractmethod - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - pass - - @abstractmethod - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - pass - - @abstractmethod - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - pass - - @abstractmethod - async def setup_chains(self) -> bool: - """Настроить цепочки firewall""" - pass - - -class IptablesFirewall(FirewallInterface): - """Реализация для iptables""" - - def __init__(self, config: Dict): - self.chain = config.get('chain', 'INPUT') - self.target = config.get('target', 'DROP') - self.table = config.get('iptables', {}).get('table', 'filter') - self.comment = "PyGuardian-ban" - - async def _run_command(self, command: List[str]) -> tuple[bool, str]: - """Выполнить команду iptables""" - try: - result = await asyncio.create_subprocess_exec( - *command, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await result.communicate() - - if result.returncode == 0: - return True, stdout.decode().strip() - else: - error_msg = stderr.decode().strip() - logger.error(f"Ошибка выполнения команды {' '.join(command)}: {error_msg}") - return False, error_msg - - except Exception as e: - logger.error(f"Исключение при выполнении команды {' '.join(command)}: {e}") - return False, str(e) - - async def setup_chains(self) -> bool: - """Настроить цепочки iptables""" - try: - # Создаем специальную цепочку для PyGuardian если не существует - pyguardian_chain = "PYGUARDIAN" - - # Проверяем, существует ли цепочка - success, _ = await self._run_command([ - "iptables", "-t", self.table, "-L", pyguardian_chain, "-n" - ]) - - if not success: - # Создаем цепочку - success, _ = await self._run_command([ - "iptables", "-t", self.table, "-N", pyguardian_chain - ]) - if not success: - return False - - logger.info(f"Создана цепочка {pyguardian_chain}") - - # Проверяем, есть ли правило перехода в нашу цепочку - success, output = await self._run_command([ - "iptables", "-t", self.table, "-L", self.chain, "-n", "--line-numbers" - ]) - - if success and pyguardian_chain not in output: - # Добавляем правило в начало цепочки INPUT - success, _ = await self._run_command([ - "iptables", "-t", self.table, "-I", self.chain, "1", - "-j", pyguardian_chain - ]) - if success: - logger.info(f"Добавлено правило перехода в цепочку {pyguardian_chain}") - else: - return False - - return True - - except Exception as e: - logger.error(f"Ошибка настройки цепочек iptables: {e}") - return False - - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес через iptables""" - try: - # Проверяем, не забанен ли уже - if await self.is_banned(ip): - logger.warning(f"IP {ip} уже забанен в iptables") - return True - - # Добавляем правило блокировки - command = [ - "iptables", "-t", self.table, "-A", "PYGUARDIAN", - "-s", ip, "-j", self.target, - "-m", "comment", "--comment", self.comment - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} заблокирован в iptables") - return True - else: - logger.error(f"Не удалось заблокировать IP {ip}: {error}") - return False - - except Exception as e: - logger.error(f"Ошибка при блокировке IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - try: - # Находим и удаляем правило - command = [ - "iptables", "-t", self.table, "-D", "PYGUARDIAN", - "-s", ip, "-j", self.target, - "-m", "comment", "--comment", self.comment - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} разблокирован в iptables") - return True - else: - # Возможно, правило уже удалено - logger.warning(f"Не удалось удалить правило для IP {ip}: {error}") - return True - - except Exception as e: - logger.error(f"Ошибка при разблокировке IP {ip}: {e}") - return False - - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - try: - command = [ - "iptables", "-t", self.table, "-L", "PYGUARDIAN", "-n" - ] - - success, output = await self._run_command(command) - if success: - return ip in output and self.comment in output - else: - return False - - except Exception as e: - logger.error(f"Ошибка при проверке IP {ip}: {e}") - return False - - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - try: - command = [ - "iptables", "-t", self.table, "-L", "PYGUARDIAN", "-n" - ] - - success, output = await self._run_command(command) - if not success: - return [] - - banned_ips = [] - for line in output.split('\n'): - if self.comment in line and self.target in line: - parts = line.split() - if len(parts) >= 4: - source_ip = parts[3] - if '/' not in source_ip: # Исключаем сети - banned_ips.append(source_ip) - - return banned_ips - - except Exception as e: - logger.error(f"Ошибка при получении списка забаненных IP: {e}") - return [] - - -class NftablesFirewall(FirewallInterface): - """Реализация для nftables""" - - def __init__(self, config: Dict): - self.table = config.get('nftables', {}).get('table', 'inet pyguardian') - self.chain = config.get('nftables', {}).get('chain', 'input') - self.set_name = "banned_ips" - - async def _run_command(self, command: List[str]) -> tuple[bool, str]: - """Выполнить команду nft""" - try: - result = await asyncio.create_subprocess_exec( - *command, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await result.communicate() - - if result.returncode == 0: - return True, stdout.decode().strip() - else: - error_msg = stderr.decode().strip() - logger.error(f"Ошибка выполнения команды {' '.join(command)}: {error_msg}") - return False, error_msg - - except Exception as e: - logger.error(f"Исключение при выполнении команды {' '.join(command)}: {e}") - return False, str(e) - - async def setup_chains(self) -> bool: - """Настроить таблицу и цепочку nftables""" - try: - # Создаем таблицу - success, _ = await self._run_command([ - "nft", "create", "table", self.table - ]) - # Игнорируем ошибку, если таблица уже существует - - # Создаем set для хранения IP адресов - success, _ = await self._run_command([ - "nft", "create", "set", f"{self.table}", self.set_name, - "{ type ipv4_addr; }" - ]) - # Игнорируем ошибку, если set уже существует - - # Создаем цепочку - success, _ = await self._run_command([ - "nft", "create", "chain", f"{self.table}", self.chain, - "{ type filter hook input priority 0; policy accept; }" - ]) - # Игнорируем ошибку, если цепочка уже существует - - # Добавляем правило блокировки для IP из set - success, _ = await self._run_command([ - "nft", "add", "rule", f"{self.table}", self.chain, - "ip", "saddr", f"@{self.set_name}", "drop" - ]) - - logger.info("Настройка nftables выполнена успешно") - return True - - except Exception as e: - logger.error(f"Ошибка настройки nftables: {e}") - return False - - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес через nftables""" - try: - command = [ - "nft", "add", "element", f"{self.table}", self.set_name, - f"{{ {ip} }}" - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} заблокирован в nftables") - return True - else: - logger.error(f"Не удалось заблокировать IP {ip}: {error}") - return False - - except Exception as e: - logger.error(f"Ошибка при блокировке IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - try: - command = [ - "nft", "delete", "element", f"{self.table}", self.set_name, - f"{{ {ip} }}" - ] - - success, error = await self._run_command(command) - if success: - logger.info(f"IP {ip} разблокирован в nftables") - return True - else: - logger.warning(f"Не удалось удалить IP {ip}: {error}") - return True # Возможно, IP уже не в списке - - except Exception as e: - logger.error(f"Ошибка при разблокировке IP {ip}: {e}") - return False - - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - try: - banned_ips = await self.list_banned_ips() - return ip in banned_ips - - except Exception as e: - logger.error(f"Ошибка при проверке IP {ip}: {e}") - return False - - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - try: - command = [ - "nft", "list", "set", f"{self.table}", self.set_name - ] - - success, output = await self._run_command(command) - if not success: - return [] - - banned_ips = [] - in_elements = False - - for line in output.split('\n'): - line = line.strip() - if 'elements = {' in line: - in_elements = True - # Проверяем, есть ли IP на той же строке - if '}' in line: - elements_part = line.split('{')[1].split('}')[0] - banned_ips.extend([ip.strip() for ip in elements_part.split(',') if ip.strip()]) - break - elif in_elements: - if '}' in line: - elements_part = line.split('}')[0] - banned_ips.extend([ip.strip() for ip in elements_part.split(',') if ip.strip()]) - break - else: - banned_ips.extend([ip.strip() for ip in line.split(',') if ip.strip()]) - - return [ip for ip in banned_ips if ip and ip != ''] - - except Exception as e: - logger.error(f"Ошибка при получении списка забаненных IP: {e}") - return [] - - -class FirewallManager: - """Менеджер для управления firewall""" - - def __init__(self, config: Dict): - self.config = config - backend = config.get('backend', 'iptables').lower() - - if backend == 'iptables': - self.firewall = IptablesFirewall(config) - elif backend == 'nftables': - self.firewall = NftablesFirewall(config) - else: - raise ValueError(f"Неподдерживаемый backend: {backend}") - - self.backend = backend - logger.info(f"Инициализирован {backend} firewall") - - async def setup(self) -> bool: - """Настроить firewall""" - return await self.firewall.setup_chains() - - async def ban_ip(self, ip: str) -> bool: - """Забанить IP адрес""" - return await self.firewall.ban_ip(ip) - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - return await self.firewall.unban_ip(ip) - - async def is_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - return await self.firewall.is_banned(ip) - - async def list_banned_ips(self) -> List[str]: - """Получить список забаненных IP""" - return await self.firewall.list_banned_ips() - - async def get_status(self) -> Dict: - """Получить статус firewall""" - try: - banned_ips = await self.list_banned_ips() - return { - 'backend': self.backend, - 'active': True, - 'banned_count': len(banned_ips), - 'banned_ips': banned_ips[:10] # Первые 10 для отображения - } - except Exception as e: - logger.error(f"Ошибка получения статуса firewall: {e}") - return { - 'backend': self.backend, - 'active': False, - 'error': str(e) - } - - async def cleanup_expired_bans(self, valid_ips: List[str]) -> int: - """Очистить firewall от IP, которые больше не должны быть забанены""" - try: - current_banned = await self.list_banned_ips() - removed_count = 0 - - for ip in current_banned: - if ip not in valid_ips: - if await self.unban_ip(ip): - removed_count += 1 - logger.info(f"Удален устаревший бан для IP {ip}") - - return removed_count - - except Exception as e: - logger.error(f"Ошибка очистки устаревших банов: {e}") - return 0 \ No newline at end of file diff --git a/.history/src/monitor_20251125194654.py b/.history/src/monitor_20251125194654.py deleted file mode 100644 index aead7d2..0000000 --- a/.history/src/monitor_20251125194654.py +++ /dev/null @@ -1,525 +0,0 @@ -""" -Monitor module для PyGuardian -Мониторинг auth.log в реальном времени и детекция атак -""" - -import asyncio -import aiofiles -import re -import logging -from datetime import datetime -from typing import Dict, List, Optional, Callable -from dataclasses import dataclass -from pathlib import Path - -logger = logging.getLogger(__name__) - - -@dataclass -class LogEvent: - """Структура для события в логе""" - timestamp: datetime - ip_address: str - username: Optional[str] - event_type: str - log_line: str - is_success: bool = False - - -class LogParser: - """Парсер для auth.log""" - - def __init__(self, patterns: List[str]): - self.failed_patterns = patterns - - # Компилируем регулярные выражения для различных типов событий - self.patterns = { - 'failed_password': re.compile( - r'Failed password for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'invalid_user': re.compile( - r'Invalid user (\w+) from ([\d.]+)' - ), - 'authentication_failure': re.compile( - r'authentication failure.*ruser=(\w*)\s+rhost=([\d.]+)' - ), - 'too_many_failures': re.compile( - r'Too many authentication failures for (\w+) from ([\d.]+)' - ), - 'failed_publickey': re.compile( - r'Failed publickey for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'connection_closed': re.compile( - r'Connection closed by authenticating user (\w+) ([\d.]+)' - ), - 'accepted_password': re.compile( - r'Accepted password for (\w+) from ([\d.]+)' - ), - 'accepted_publickey': re.compile( - r'Accepted publickey for (\w+) from ([\d.]+)' - ) - } - - def parse_line(self, line: str) -> Optional[LogEvent]: - """Парсинг строки лога""" - try: - # Извлекаем timestamp - timestamp = self._parse_timestamp(line) - if not timestamp: - return None - - # Проверяем успешные входы - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - match = pattern.search(line) - if match: - username, ip = match.groups() - return LogEvent( - timestamp=timestamp, - ip_address=ip, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=True - ) - - # Проверяем атаки - for pattern in self.failed_patterns: - if pattern.lower() in line.lower(): - event = self._parse_failed_event(line, timestamp) - if event: - return event - - return None - - except Exception as e: - logger.error(f"Ошибка парсинга строки '{line[:100]}...': {e}") - return None - - def _parse_timestamp(self, line: str) -> Optional[datetime]: - """Извлечение timestamp из строки лога""" - try: - # Стандартный формат syslog: "Nov 25 14:30:15" - timestamp_pattern = re.compile( - r'^(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})' - ) - match = timestamp_pattern.search(line) - if match: - timestamp_str = match.group(1) - # Добавляем текущий год - current_year = datetime.now().year - timestamp_str = f"{current_year} {timestamp_str}" - return datetime.strptime(timestamp_str, "%Y %b %d %H:%M:%S") - return None - except Exception: - return None - - def _parse_failed_event(self, line: str, timestamp: datetime) -> Optional[LogEvent]: - """Парсинг событий атак""" - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - continue - - match = pattern.search(line) - if match: - groups = match.groups() - if len(groups) >= 2: - username = groups[0] if groups[0] else "unknown" - ip_address = groups[1] - - return LogEvent( - timestamp=timestamp, - ip_address=ip_address, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=False - ) - - # Если не удалось распарсить конкретным паттерном, - # ищем IP в строке - ip_pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') - ip_match = ip_pattern.search(line) - - if ip_match: - return LogEvent( - timestamp=timestamp, - ip_address=ip_match.group(1), - username="unknown", - event_type="generic_failure", - log_line=line.strip(), - is_success=False - ) - - return None - - -class LogMonitor: - """Мониторинг auth.log файла""" - - def __init__(self, config: Dict, event_callback: Optional[Callable] = None): - self.log_path = config.get('auth_log_path', '/var/log/auth.log') - self.check_interval = config.get('check_interval', 1.0) - self.parser = LogParser(config.get('failed_patterns', [])) - - self.event_callback = event_callback - self.running = False - self.file_position = 0 - self.last_inode = None - - # Статистика - self.stats = { - 'lines_processed': 0, - 'events_detected': 0, - 'last_event_time': None, - 'start_time': datetime.now() - } - - async def start(self) -> None: - """Запуск мониторинга""" - if self.running: - logger.warning("Мониторинг уже запущен") - return - - self.running = True - logger.info(f"Запуск мониторинга файла {self.log_path}") - - # Устанавливаем позицию в конец файла при запуске - await self._init_file_position() - - try: - while self.running: - await self._check_log_file() - await asyncio.sleep(self.check_interval) - except Exception as e: - logger.error(f"Ошибка в цикле мониторинга: {e}") - finally: - self.running = False - - async def stop(self) -> None: - """Остановка мониторинга""" - logger.info("Остановка мониторинга") - self.running = False - - async def _init_file_position(self) -> None: - """Инициализация позиции в файле""" - try: - if Path(self.log_path).exists(): - stat = Path(self.log_path).stat() - self.file_position = stat.st_size - self.last_inode = stat.st_ino - logger.info(f"Начальная позиция в файле: {self.file_position}") - else: - logger.warning(f"Лог файл {self.log_path} не найден") - self.file_position = 0 - self.last_inode = None - except Exception as e: - logger.error(f"Ошибка инициализации позиции файла: {e}") - self.file_position = 0 - self.last_inode = None - - async def _check_log_file(self) -> None: - """Проверка изменений в лог файле""" - try: - if not Path(self.log_path).exists(): - logger.warning(f"Лог файл {self.log_path} не существует") - return - - stat = Path(self.log_path).stat() - current_inode = stat.st_ino - current_size = stat.st_size - - # Проверяем, не был ли файл ротирован - if self.last_inode is not None and current_inode != self.last_inode: - logger.info("Обнаружена ротация лог файла") - self.file_position = 0 - self.last_inode = current_inode - - # Проверяем, есть ли новые данные - if current_size > self.file_position: - await self._process_new_lines(current_size) - elif current_size < self.file_position: - # Файл был усечен - logger.info("Файл был усечен, сброс позиции") - self.file_position = 0 - await self._process_new_lines(current_size) - - self.last_inode = current_inode - - except Exception as e: - logger.error(f"Ошибка проверки лог файла: {e}") - - async def _process_new_lines(self, current_size: int) -> None: - """Обработка новых строк в файле""" - try: - async with aiofiles.open(self.log_path, 'r', encoding='utf-8', errors='ignore') as file: - await file.seek(self.file_position) - - while True: - line = await file.readline() - if not line: - break - - self.stats['lines_processed'] += 1 - - # Парсим строку - event = self.parser.parse_line(line) - if event: - self.stats['events_detected'] += 1 - self.stats['last_event_time'] = event.timestamp - - logger.debug(f"Обнаружено событие: {event.event_type} from {event.ip_address}") - - # Отправляем событие в callback - if self.event_callback: - try: - if asyncio.iscoroutinefunction(self.event_callback): - await self.event_callback(event) - else: - self.event_callback(event) - except Exception as e: - logger.error(f"Ошибка в callback: {e}") - - # Обновляем позицию - self.file_position = await file.tell() - - except Exception as e: - logger.error(f"Ошибка обработки новых строк: {e}") - - def get_stats(self) -> Dict: - """Получение статистики мониторинга""" - uptime = datetime.now() - self.stats['start_time'] - - return { - 'running': self.running, - 'log_path': self.log_path, - 'file_position': self.file_position, - 'lines_processed': self.stats['lines_processed'], - 'events_detected': self.stats['events_detected'], - 'last_event_time': self.stats['last_event_time'], - 'uptime_seconds': int(uptime.total_seconds()), - 'check_interval': self.check_interval - } - - async def test_patterns(self, test_lines: List[str]) -> List[LogEvent]: - """Тестирование паттернов на примерах строк""" - events = [] - for line in test_lines: - event = self.parser.parse_line(line) - if event: - events.append(event) - return events - - -class AttackDetector: - """Детектор атак на основе событий""" - - def __init__(self, storage, firewall_manager, config: Dict): - self.storage = storage - self.firewall_manager = firewall_manager - self.config = config - - self.max_attempts = config.get('max_attempts', 5) - self.time_window = config.get('time_window', 60) - self.unban_time = config.get('unban_time', 3600) - self.whitelist = config.get('whitelist', []) - - # Callback для уведомлений - self.ban_callback: Optional[Callable] = None - self.unban_callback: Optional[Callable] = None - - def set_callbacks(self, ban_callback: Optional[Callable] = None, - unban_callback: Optional[Callable] = None) -> None: - """Установка callback для уведомлений""" - self.ban_callback = ban_callback - self.unban_callback = unban_callback - - async def process_event(self, event: LogEvent) -> None: - """Обработка события из лога""" - try: - # Добавляем событие в базу данных - if event.is_success: - await self.storage.add_successful_login( - event.ip_address, - event.username or "unknown" - ) - logger.info(f"Успешный вход: {event.username}@{event.ip_address}") - else: - await self.storage.add_attack_attempt( - event.ip_address, - event.username or "unknown", - event.event_type, - event.log_line, - event.timestamp - ) - - # Проверяем, нужно ли банить IP - await self._check_and_ban_ip(event.ip_address) - - except Exception as e: - logger.error(f"Ошибка обработки события: {e}") - - async def _check_and_ban_ip(self, ip: str) -> None: - """Проверка и бан IP при превышении лимита""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.info(f"IP {ip} в белом списке, пропускаем") - return - - # Проверяем, не забанен ли уже - if await self.storage.is_ip_banned(ip): - logger.debug(f"IP {ip} уже забанен") - return - - # Получаем количество попыток за время окна - attempts = await self.storage.get_attack_count_for_ip(ip, self.time_window) - - if attempts >= self.max_attempts: - # Баним IP - reason = f"Превышен лимит попыток: {attempts}/{self.max_attempts} за {self.time_window}с" - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=False, attempts_count=attempts - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.warning(f"IP {ip} забанен: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': attempts, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в ban callback: {e}") - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - else: - logger.error(f"Не удалось записать бан IP {ip} в базу данных") - - except Exception as e: - logger.error(f"Ошибка проверки IP {ip} для бана: {e}") - - async def process_unban(self, ip: str) -> bool: - """Разбан IP адреса""" - try: - # Разбаниваем в базе данных - db_success = await self.storage.unban_ip(ip) - - # Разбаниваем в firewall - firewall_success = await self.firewall_manager.unban_ip(ip) - - if db_success and firewall_success: - logger.info(f"IP {ip} успешно разбанен") - - # Уведомление через callback - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в unban callback: {e}") - - return True - else: - logger.error(f"Ошибка разбана IP {ip}") - return False - - except Exception as e: - logger.error(f"Ошибка разбана IP {ip}: {e}") - return False - - async def check_expired_bans(self) -> None: - """Проверка и автоматический разбан истекших IP""" - try: - expired_ips = await self.storage.get_expired_bans() - - for ip in expired_ips: - success = await self.process_unban(ip) - if success: - logger.info(f"IP {ip} автоматически разбанен (истек срок)") - - # Уведомление об автоматическом разбане - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в auto unban callback: {e}") - - except Exception as e: - logger.error(f"Ошибка проверки истекших банов: {e}") - - async def manual_ban(self, ip: str, reason: str = "Ручная блокировка") -> bool: - """Ручной бан IP адреса""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.warning(f"Попытка заблокировать IP {ip} из белого списка") - return False - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=True - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.info(f"IP {ip} ручной бан: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': 0, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в manual ban callback: {e}") - - return True - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - return False - else: - logger.error(f"Не удалось записать ручной бан IP {ip} в базу данных") - return False - - except Exception as e: - logger.error(f"Ошибка ручного бана IP {ip}: {e}") - return False \ No newline at end of file diff --git a/.history/src/monitor_20251125200036.py b/.history/src/monitor_20251125200036.py deleted file mode 100644 index 2176eaf..0000000 --- a/.history/src/monitor_20251125200036.py +++ /dev/null @@ -1,526 +0,0 @@ -""" -Monitor module для PyGuardian -Мониторинг auth.log в реальном времени и детекция атак -""" - -import asyncio -import aiofiles -import re -import logging -from datetime import datetime -from typing import Dict, List, Optional, Callable -from dataclasses import dataclass -from pathlib import Path - -logger = logging.getLogger(__name__) - - -@dataclass -class LogEvent: - """Структура для события в логе""" - timestamp: datetime - ip_address: str - username: Optional[str] - event_type: str - log_line: str - is_success: bool = False - - -class LogParser: - """Парсер для auth.log""" - - def __init__(self, patterns: List[str]): - self.failed_patterns = patterns - - # Компилируем регулярные выражения для различных типов событий - self.patterns = { - 'failed_password': re.compile( - r'Failed password for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'invalid_user': re.compile( - r'Invalid user (\w+) from ([\d.]+)' - ), - 'authentication_failure': re.compile( - r'authentication failure.*ruser=(\w*)\s+rhost=([\d.]+)' - ), - 'too_many_failures': re.compile( - r'Too many authentication failures for (\w+) from ([\d.]+)' - ), - 'failed_publickey': re.compile( - r'Failed publickey for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'connection_closed': re.compile( - r'Connection closed by authenticating user (\w+) ([\d.]+)' - ), - 'accepted_password': re.compile( - r'Accepted password for (\w+) from ([\d.]+)' - ), - 'accepted_publickey': re.compile( - r'Accepted publickey for (\w+) from ([\d.]+)' - ) - } - - def parse_line(self, line: str) -> Optional[LogEvent]: - """Парсинг строки лога""" - try: - # Извлекаем timestamp - timestamp = self._parse_timestamp(line) - if not timestamp: - return None - - # Проверяем успешные входы - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - match = pattern.search(line) - if match: - username, ip = match.groups() - return LogEvent( - timestamp=timestamp, - ip_address=ip, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=True - ) - - # Проверяем атаки - for pattern in self.failed_patterns: - if pattern.lower() in line.lower(): - event = self._parse_failed_event(line, timestamp) - if event: - return event - - return None - - except Exception as e: - logger.error(f"Ошибка парсинга строки '{line[:100]}...': {e}") - return None - - def _parse_timestamp(self, line: str) -> Optional[datetime]: - """Извлечение timestamp из строки лога""" - try: - # Стандартный формат syslog: "Nov 25 14:30:15" - timestamp_pattern = re.compile( - r'^(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})' - ) - match = timestamp_pattern.search(line) - if match: - timestamp_str = match.group(1) - # Добавляем текущий год - current_year = datetime.now().year - timestamp_str = f"{current_year} {timestamp_str}" - return datetime.strptime(timestamp_str, "%Y %b %d %H:%M:%S") - return None - except Exception: - return None - - def _parse_failed_event(self, line: str, timestamp: datetime) -> Optional[LogEvent]: - """Парсинг событий атак""" - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - continue - - match = pattern.search(line) - if match: - groups = match.groups() - if len(groups) >= 2: - username = groups[0] if groups[0] else "unknown" - ip_address = groups[1] - - return LogEvent( - timestamp=timestamp, - ip_address=ip_address, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=False - ) - - # Если не удалось распарсить конкретным паттерном, - # ищем IP в строке - ip_pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') - ip_match = ip_pattern.search(line) - - if ip_match: - return LogEvent( - timestamp=timestamp, - ip_address=ip_match.group(1), - username="unknown", - event_type="generic_failure", - log_line=line.strip(), - is_success=False - ) - - return None - - -class LogMonitor: - """Мониторинг auth.log файла""" - - def __init__(self, config: Dict, event_callback: Optional[Callable] = None): - self.log_path = config.get('auth_log_path', '/var/log/auth.log') - self.check_interval = config.get('check_interval', 1.0) - self.parser = LogParser(config.get('failed_patterns', [])) - - self.event_callback = event_callback - self.running = False - self.file_position = 0 - self.last_inode = None - - # Статистика - self.stats = { - 'lines_processed': 0, - 'events_detected': 0, - 'last_event_time': None, - 'start_time': datetime.now() - } - - async def start(self) -> None: - """Запуск мониторинга""" - if self.running: - logger.warning("Мониторинг уже запущен") - return - - self.running = True - logger.info(f"Запуск мониторинга файла {self.log_path}") - - # Устанавливаем позицию в конец файла при запуске - await self._init_file_position() - - try: - while self.running: - await self._check_log_file() - await asyncio.sleep(self.check_interval) - except Exception as e: - logger.error(f"Ошибка в цикле мониторинга: {e}") - finally: - self.running = False - - async def stop(self) -> None: - """Остановка мониторинга""" - logger.info("Остановка мониторинга") - self.running = False - - async def _init_file_position(self) -> None: - """Инициализация позиции в файле""" - try: - if Path(self.log_path).exists(): - stat = Path(self.log_path).stat() - self.file_position = stat.st_size - self.last_inode = stat.st_ino - logger.info(f"Начальная позиция в файле: {self.file_position}") - else: - logger.warning(f"Лог файл {self.log_path} не найден") - self.file_position = 0 - self.last_inode = None - except Exception as e: - logger.error(f"Ошибка инициализации позиции файла: {e}") - self.file_position = 0 - self.last_inode = None - - async def _check_log_file(self) -> None: - """Проверка изменений в лог файле""" - try: - if not Path(self.log_path).exists(): - logger.warning(f"Лог файл {self.log_path} не существует") - return - - stat = Path(self.log_path).stat() - current_inode = stat.st_ino - current_size = stat.st_size - - # Проверяем, не был ли файл ротирован - if self.last_inode is not None and current_inode != self.last_inode: - logger.info("Обнаружена ротация лог файла") - self.file_position = 0 - self.last_inode = current_inode - - # Проверяем, есть ли новые данные - if current_size > self.file_position: - await self._process_new_lines(current_size) - elif current_size < self.file_position: - # Файл был усечен - logger.info("Файл был усечен, сброс позиции") - self.file_position = 0 - await self._process_new_lines(current_size) - - self.last_inode = current_inode - - except Exception as e: - logger.error(f"Ошибка проверки лог файла: {e}") - - async def _process_new_lines(self, current_size: int) -> None: - """Обработка новых строк в файле""" - try: - async with aiofiles.open(self.log_path, 'r', encoding='utf-8', errors='ignore') as file: - await file.seek(self.file_position) - - while True: - line = await file.readline() - if not line: - break - - self.stats['lines_processed'] += 1 - - # Парсим строку - event = self.parser.parse_line(line) - if event: - self.stats['events_detected'] += 1 - self.stats['last_event_time'] = event.timestamp - - logger.debug(f"Обнаружено событие: {event.event_type} from {event.ip_address}") - - # Отправляем событие в callback - if self.event_callback: - try: - if asyncio.iscoroutinefunction(self.event_callback): - await self.event_callback(event) - else: - self.event_callback(event) - except Exception as e: - logger.error(f"Ошибка в callback: {e}") - - # Обновляем позицию - self.file_position = await file.tell() - - except Exception as e: - logger.error(f"Ошибка обработки новых строк: {e}") - - def get_stats(self) -> Dict: - """Получение статистики мониторинга""" - uptime = datetime.now() - self.stats['start_time'] - - return { - 'running': self.running, - 'log_path': self.log_path, - 'file_position': self.file_position, - 'lines_processed': self.stats['lines_processed'], - 'events_detected': self.stats['events_detected'], - 'last_event_time': self.stats['last_event_time'], - 'uptime_seconds': int(uptime.total_seconds()), - 'check_interval': self.check_interval - } - - async def test_patterns(self, test_lines: List[str]) -> List[LogEvent]: - """Тестирование паттернов на примерах строк""" - events = [] - for line in test_lines: - event = self.parser.parse_line(line) - if event: - events.append(event) - return events - - -class AttackDetector: - """Детектор атак на основе событий""" - - def __init__(self, storage, firewall_manager, security_manager, config: Dict): - self.storage = storage - self.firewall_manager = firewall_manager - self.security_manager = security_manager - self.config = config - - self.max_attempts = config.get('max_attempts', 5) - self.time_window = config.get('time_window', 60) - self.unban_time = config.get('unban_time', 3600) - self.whitelist = config.get('whitelist', []) - - # Callback для уведомлений - self.ban_callback: Optional[Callable] = None - self.unban_callback: Optional[Callable] = None - - def set_callbacks(self, ban_callback: Optional[Callable] = None, - unban_callback: Optional[Callable] = None) -> None: - """Установка callback для уведомлений""" - self.ban_callback = ban_callback - self.unban_callback = unban_callback - - async def process_event(self, event: LogEvent) -> None: - """Обработка события из лога""" - try: - # Добавляем событие в базу данных - if event.is_success: - await self.storage.add_successful_login( - event.ip_address, - event.username or "unknown" - ) - logger.info(f"Успешный вход: {event.username}@{event.ip_address}") - else: - await self.storage.add_attack_attempt( - event.ip_address, - event.username or "unknown", - event.event_type, - event.log_line, - event.timestamp - ) - - # Проверяем, нужно ли банить IP - await self._check_and_ban_ip(event.ip_address) - - except Exception as e: - logger.error(f"Ошибка обработки события: {e}") - - async def _check_and_ban_ip(self, ip: str) -> None: - """Проверка и бан IP при превышении лимита""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.info(f"IP {ip} в белом списке, пропускаем") - return - - # Проверяем, не забанен ли уже - if await self.storage.is_ip_banned(ip): - logger.debug(f"IP {ip} уже забанен") - return - - # Получаем количество попыток за время окна - attempts = await self.storage.get_attack_count_for_ip(ip, self.time_window) - - if attempts >= self.max_attempts: - # Баним IP - reason = f"Превышен лимит попыток: {attempts}/{self.max_attempts} за {self.time_window}с" - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=False, attempts_count=attempts - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.warning(f"IP {ip} забанен: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': attempts, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в ban callback: {e}") - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - else: - logger.error(f"Не удалось записать бан IP {ip} в базу данных") - - except Exception as e: - logger.error(f"Ошибка проверки IP {ip} для бана: {e}") - - async def process_unban(self, ip: str) -> bool: - """Разбан IP адреса""" - try: - # Разбаниваем в базе данных - db_success = await self.storage.unban_ip(ip) - - # Разбаниваем в firewall - firewall_success = await self.firewall_manager.unban_ip(ip) - - if db_success and firewall_success: - logger.info(f"IP {ip} успешно разбанен") - - # Уведомление через callback - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в unban callback: {e}") - - return True - else: - logger.error(f"Ошибка разбана IP {ip}") - return False - - except Exception as e: - logger.error(f"Ошибка разбана IP {ip}: {e}") - return False - - async def check_expired_bans(self) -> None: - """Проверка и автоматический разбан истекших IP""" - try: - expired_ips = await self.storage.get_expired_bans() - - for ip in expired_ips: - success = await self.process_unban(ip) - if success: - logger.info(f"IP {ip} автоматически разбанен (истек срок)") - - # Уведомление об автоматическом разбане - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в auto unban callback: {e}") - - except Exception as e: - logger.error(f"Ошибка проверки истекших банов: {e}") - - async def manual_ban(self, ip: str, reason: str = "Ручная блокировка") -> bool: - """Ручной бан IP адреса""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.warning(f"Попытка заблокировать IP {ip} из белого списка") - return False - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=True - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.info(f"IP {ip} ручной бан: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': 0, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в manual ban callback: {e}") - - return True - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - return False - else: - logger.error(f"Не удалось записать ручной бан IP {ip} в базу данных") - return False - - except Exception as e: - logger.error(f"Ошибка ручного бана IP {ip}: {e}") - return False \ No newline at end of file diff --git a/.history/src/monitor_20251125200048.py b/.history/src/monitor_20251125200048.py deleted file mode 100644 index a7bdd5e..0000000 --- a/.history/src/monitor_20251125200048.py +++ /dev/null @@ -1,530 +0,0 @@ -""" -Monitor module для PyGuardian -Мониторинг auth.log в реальном времени и детекция атак -""" - -import asyncio -import aiofiles -import re -import logging -from datetime import datetime -from typing import Dict, List, Optional, Callable -from dataclasses import dataclass -from pathlib import Path - -logger = logging.getLogger(__name__) - - -@dataclass -class LogEvent: - """Структура для события в логе""" - timestamp: datetime - ip_address: str - username: Optional[str] - event_type: str - log_line: str - is_success: bool = False - - -class LogParser: - """Парсер для auth.log""" - - def __init__(self, patterns: List[str]): - self.failed_patterns = patterns - - # Компилируем регулярные выражения для различных типов событий - self.patterns = { - 'failed_password': re.compile( - r'Failed password for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'invalid_user': re.compile( - r'Invalid user (\w+) from ([\d.]+)' - ), - 'authentication_failure': re.compile( - r'authentication failure.*ruser=(\w*)\s+rhost=([\d.]+)' - ), - 'too_many_failures': re.compile( - r'Too many authentication failures for (\w+) from ([\d.]+)' - ), - 'failed_publickey': re.compile( - r'Failed publickey for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'connection_closed': re.compile( - r'Connection closed by authenticating user (\w+) ([\d.]+)' - ), - 'accepted_password': re.compile( - r'Accepted password for (\w+) from ([\d.]+)' - ), - 'accepted_publickey': re.compile( - r'Accepted publickey for (\w+) from ([\d.]+)' - ) - } - - def parse_line(self, line: str) -> Optional[LogEvent]: - """Парсинг строки лога""" - try: - # Извлекаем timestamp - timestamp = self._parse_timestamp(line) - if not timestamp: - return None - - # Проверяем успешные входы - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - match = pattern.search(line) - if match: - username, ip = match.groups() - return LogEvent( - timestamp=timestamp, - ip_address=ip, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=True - ) - - # Проверяем атаки - for pattern in self.failed_patterns: - if pattern.lower() in line.lower(): - event = self._parse_failed_event(line, timestamp) - if event: - return event - - return None - - except Exception as e: - logger.error(f"Ошибка парсинга строки '{line[:100]}...': {e}") - return None - - def _parse_timestamp(self, line: str) -> Optional[datetime]: - """Извлечение timestamp из строки лога""" - try: - # Стандартный формат syslog: "Nov 25 14:30:15" - timestamp_pattern = re.compile( - r'^(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})' - ) - match = timestamp_pattern.search(line) - if match: - timestamp_str = match.group(1) - # Добавляем текущий год - current_year = datetime.now().year - timestamp_str = f"{current_year} {timestamp_str}" - return datetime.strptime(timestamp_str, "%Y %b %d %H:%M:%S") - return None - except Exception: - return None - - def _parse_failed_event(self, line: str, timestamp: datetime) -> Optional[LogEvent]: - """Парсинг событий атак""" - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - continue - - match = pattern.search(line) - if match: - groups = match.groups() - if len(groups) >= 2: - username = groups[0] if groups[0] else "unknown" - ip_address = groups[1] - - return LogEvent( - timestamp=timestamp, - ip_address=ip_address, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=False - ) - - # Если не удалось распарсить конкретным паттерном, - # ищем IP в строке - ip_pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') - ip_match = ip_pattern.search(line) - - if ip_match: - return LogEvent( - timestamp=timestamp, - ip_address=ip_match.group(1), - username="unknown", - event_type="generic_failure", - log_line=line.strip(), - is_success=False - ) - - return None - - -class LogMonitor: - """Мониторинг auth.log файла""" - - def __init__(self, config: Dict, event_callback: Optional[Callable] = None): - self.log_path = config.get('auth_log_path', '/var/log/auth.log') - self.check_interval = config.get('check_interval', 1.0) - self.parser = LogParser(config.get('failed_patterns', [])) - - self.event_callback = event_callback - self.running = False - self.file_position = 0 - self.last_inode = None - - # Статистика - self.stats = { - 'lines_processed': 0, - 'events_detected': 0, - 'last_event_time': None, - 'start_time': datetime.now() - } - - async def start(self) -> None: - """Запуск мониторинга""" - if self.running: - logger.warning("Мониторинг уже запущен") - return - - self.running = True - logger.info(f"Запуск мониторинга файла {self.log_path}") - - # Устанавливаем позицию в конец файла при запуске - await self._init_file_position() - - try: - while self.running: - await self._check_log_file() - await asyncio.sleep(self.check_interval) - except Exception as e: - logger.error(f"Ошибка в цикле мониторинга: {e}") - finally: - self.running = False - - async def stop(self) -> None: - """Остановка мониторинга""" - logger.info("Остановка мониторинга") - self.running = False - - async def _init_file_position(self) -> None: - """Инициализация позиции в файле""" - try: - if Path(self.log_path).exists(): - stat = Path(self.log_path).stat() - self.file_position = stat.st_size - self.last_inode = stat.st_ino - logger.info(f"Начальная позиция в файле: {self.file_position}") - else: - logger.warning(f"Лог файл {self.log_path} не найден") - self.file_position = 0 - self.last_inode = None - except Exception as e: - logger.error(f"Ошибка инициализации позиции файла: {e}") - self.file_position = 0 - self.last_inode = None - - async def _check_log_file(self) -> None: - """Проверка изменений в лог файле""" - try: - if not Path(self.log_path).exists(): - logger.warning(f"Лог файл {self.log_path} не существует") - return - - stat = Path(self.log_path).stat() - current_inode = stat.st_ino - current_size = stat.st_size - - # Проверяем, не был ли файл ротирован - if self.last_inode is not None and current_inode != self.last_inode: - logger.info("Обнаружена ротация лог файла") - self.file_position = 0 - self.last_inode = current_inode - - # Проверяем, есть ли новые данные - if current_size > self.file_position: - await self._process_new_lines(current_size) - elif current_size < self.file_position: - # Файл был усечен - logger.info("Файл был усечен, сброс позиции") - self.file_position = 0 - await self._process_new_lines(current_size) - - self.last_inode = current_inode - - except Exception as e: - logger.error(f"Ошибка проверки лог файла: {e}") - - async def _process_new_lines(self, current_size: int) -> None: - """Обработка новых строк в файле""" - try: - async with aiofiles.open(self.log_path, 'r', encoding='utf-8', errors='ignore') as file: - await file.seek(self.file_position) - - while True: - line = await file.readline() - if not line: - break - - self.stats['lines_processed'] += 1 - - # Парсим строку - event = self.parser.parse_line(line) - if event: - self.stats['events_detected'] += 1 - self.stats['last_event_time'] = event.timestamp - - logger.debug(f"Обнаружено событие: {event.event_type} from {event.ip_address}") - - # Отправляем событие в callback - if self.event_callback: - try: - if asyncio.iscoroutinefunction(self.event_callback): - await self.event_callback(event) - else: - self.event_callback(event) - except Exception as e: - logger.error(f"Ошибка в callback: {e}") - - # Обновляем позицию - self.file_position = await file.tell() - - except Exception as e: - logger.error(f"Ошибка обработки новых строк: {e}") - - def get_stats(self) -> Dict: - """Получение статистики мониторинга""" - uptime = datetime.now() - self.stats['start_time'] - - return { - 'running': self.running, - 'log_path': self.log_path, - 'file_position': self.file_position, - 'lines_processed': self.stats['lines_processed'], - 'events_detected': self.stats['events_detected'], - 'last_event_time': self.stats['last_event_time'], - 'uptime_seconds': int(uptime.total_seconds()), - 'check_interval': self.check_interval - } - - async def test_patterns(self, test_lines: List[str]) -> List[LogEvent]: - """Тестирование паттернов на примерах строк""" - events = [] - for line in test_lines: - event = self.parser.parse_line(line) - if event: - events.append(event) - return events - - -class AttackDetector: - """Детектор атак на основе событий""" - - def __init__(self, storage, firewall_manager, security_manager, config: Dict): - self.storage = storage - self.firewall_manager = firewall_manager - self.security_manager = security_manager - self.config = config - - self.max_attempts = config.get('max_attempts', 5) - self.time_window = config.get('time_window', 60) - self.unban_time = config.get('unban_time', 3600) - self.whitelist = config.get('whitelist', []) - - # Callback для уведомлений - self.ban_callback: Optional[Callable] = None - self.unban_callback: Optional[Callable] = None - - def set_callbacks(self, ban_callback: Optional[Callable] = None, - unban_callback: Optional[Callable] = None) -> None: - """Установка callback для уведомлений""" - self.ban_callback = ban_callback - self.unban_callback = unban_callback - - async def process_event(self, event: LogEvent) -> None: - """Обработка события из лога""" - try: - # Передаем событие в SecurityManager для глубокого анализа - await self.security_manager.analyze_login_event(event) - - # Добавляем событие в базу данных - if event.is_success: - await self.storage.add_successful_login( - event.ip_address, - event.username or "unknown", - f"login_type:{event.event_type}" - ) - logger.info(f"Успешный вход: {event.username}@{event.ip_address}") - else: - await self.storage.add_attack_attempt( - event.ip_address, - event.username or "unknown", - event.event_type, - event.log_line, - event.timestamp - ) - - # Проверяем, нужно ли банить IP (стандартная логика брутфорса) - await self._check_and_ban_ip(event.ip_address) - - except Exception as e: - logger.error(f"Ошибка обработки события: {e}") - - async def _check_and_ban_ip(self, ip: str) -> None: - """Проверка и бан IP при превышении лимита""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.info(f"IP {ip} в белом списке, пропускаем") - return - - # Проверяем, не забанен ли уже - if await self.storage.is_ip_banned(ip): - logger.debug(f"IP {ip} уже забанен") - return - - # Получаем количество попыток за время окна - attempts = await self.storage.get_attack_count_for_ip(ip, self.time_window) - - if attempts >= self.max_attempts: - # Баним IP - reason = f"Превышен лимит попыток: {attempts}/{self.max_attempts} за {self.time_window}с" - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=False, attempts_count=attempts - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.warning(f"IP {ip} забанен: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': attempts, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в ban callback: {e}") - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - else: - logger.error(f"Не удалось записать бан IP {ip} в базу данных") - - except Exception as e: - logger.error(f"Ошибка проверки IP {ip} для бана: {e}") - - async def process_unban(self, ip: str) -> bool: - """Разбан IP адреса""" - try: - # Разбаниваем в базе данных - db_success = await self.storage.unban_ip(ip) - - # Разбаниваем в firewall - firewall_success = await self.firewall_manager.unban_ip(ip) - - if db_success and firewall_success: - logger.info(f"IP {ip} успешно разбанен") - - # Уведомление через callback - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в unban callback: {e}") - - return True - else: - logger.error(f"Ошибка разбана IP {ip}") - return False - - except Exception as e: - logger.error(f"Ошибка разбана IP {ip}: {e}") - return False - - async def check_expired_bans(self) -> None: - """Проверка и автоматический разбан истекших IP""" - try: - expired_ips = await self.storage.get_expired_bans() - - for ip in expired_ips: - success = await self.process_unban(ip) - if success: - logger.info(f"IP {ip} автоматически разбанен (истек срок)") - - # Уведомление об автоматическом разбане - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в auto unban callback: {e}") - - except Exception as e: - logger.error(f"Ошибка проверки истекших банов: {e}") - - async def manual_ban(self, ip: str, reason: str = "Ручная блокировка") -> bool: - """Ручной бан IP адреса""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.warning(f"Попытка заблокировать IP {ip} из белого списка") - return False - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=True - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.info(f"IP {ip} ручной бан: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': 0, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в manual ban callback: {e}") - - return True - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - return False - else: - logger.error(f"Не удалось записать ручной бан IP {ip} в базу данных") - return False - - except Exception as e: - logger.error(f"Ошибка ручного бана IP {ip}: {e}") - return False \ No newline at end of file diff --git a/.history/src/monitor_20251125202055.py b/.history/src/monitor_20251125202055.py deleted file mode 100644 index a7bdd5e..0000000 --- a/.history/src/monitor_20251125202055.py +++ /dev/null @@ -1,530 +0,0 @@ -""" -Monitor module для PyGuardian -Мониторинг auth.log в реальном времени и детекция атак -""" - -import asyncio -import aiofiles -import re -import logging -from datetime import datetime -from typing import Dict, List, Optional, Callable -from dataclasses import dataclass -from pathlib import Path - -logger = logging.getLogger(__name__) - - -@dataclass -class LogEvent: - """Структура для события в логе""" - timestamp: datetime - ip_address: str - username: Optional[str] - event_type: str - log_line: str - is_success: bool = False - - -class LogParser: - """Парсер для auth.log""" - - def __init__(self, patterns: List[str]): - self.failed_patterns = patterns - - # Компилируем регулярные выражения для различных типов событий - self.patterns = { - 'failed_password': re.compile( - r'Failed password for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'invalid_user': re.compile( - r'Invalid user (\w+) from ([\d.]+)' - ), - 'authentication_failure': re.compile( - r'authentication failure.*ruser=(\w*)\s+rhost=([\d.]+)' - ), - 'too_many_failures': re.compile( - r'Too many authentication failures for (\w+) from ([\d.]+)' - ), - 'failed_publickey': re.compile( - r'Failed publickey for (?:invalid user )?(\w+) from ([\d.]+)' - ), - 'connection_closed': re.compile( - r'Connection closed by authenticating user (\w+) ([\d.]+)' - ), - 'accepted_password': re.compile( - r'Accepted password for (\w+) from ([\d.]+)' - ), - 'accepted_publickey': re.compile( - r'Accepted publickey for (\w+) from ([\d.]+)' - ) - } - - def parse_line(self, line: str) -> Optional[LogEvent]: - """Парсинг строки лога""" - try: - # Извлекаем timestamp - timestamp = self._parse_timestamp(line) - if not timestamp: - return None - - # Проверяем успешные входы - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - match = pattern.search(line) - if match: - username, ip = match.groups() - return LogEvent( - timestamp=timestamp, - ip_address=ip, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=True - ) - - # Проверяем атаки - for pattern in self.failed_patterns: - if pattern.lower() in line.lower(): - event = self._parse_failed_event(line, timestamp) - if event: - return event - - return None - - except Exception as e: - logger.error(f"Ошибка парсинга строки '{line[:100]}...': {e}") - return None - - def _parse_timestamp(self, line: str) -> Optional[datetime]: - """Извлечение timestamp из строки лога""" - try: - # Стандартный формат syslog: "Nov 25 14:30:15" - timestamp_pattern = re.compile( - r'^(\w{3}\s+\d{1,2}\s+\d{2}:\d{2}:\d{2})' - ) - match = timestamp_pattern.search(line) - if match: - timestamp_str = match.group(1) - # Добавляем текущий год - current_year = datetime.now().year - timestamp_str = f"{current_year} {timestamp_str}" - return datetime.strptime(timestamp_str, "%Y %b %d %H:%M:%S") - return None - except Exception: - return None - - def _parse_failed_event(self, line: str, timestamp: datetime) -> Optional[LogEvent]: - """Парсинг событий атак""" - for pattern_name, pattern in self.patterns.items(): - if 'accepted' in pattern_name.lower(): - continue - - match = pattern.search(line) - if match: - groups = match.groups() - if len(groups) >= 2: - username = groups[0] if groups[0] else "unknown" - ip_address = groups[1] - - return LogEvent( - timestamp=timestamp, - ip_address=ip_address, - username=username, - event_type=pattern_name, - log_line=line.strip(), - is_success=False - ) - - # Если не удалось распарсить конкретным паттерном, - # ищем IP в строке - ip_pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})') - ip_match = ip_pattern.search(line) - - if ip_match: - return LogEvent( - timestamp=timestamp, - ip_address=ip_match.group(1), - username="unknown", - event_type="generic_failure", - log_line=line.strip(), - is_success=False - ) - - return None - - -class LogMonitor: - """Мониторинг auth.log файла""" - - def __init__(self, config: Dict, event_callback: Optional[Callable] = None): - self.log_path = config.get('auth_log_path', '/var/log/auth.log') - self.check_interval = config.get('check_interval', 1.0) - self.parser = LogParser(config.get('failed_patterns', [])) - - self.event_callback = event_callback - self.running = False - self.file_position = 0 - self.last_inode = None - - # Статистика - self.stats = { - 'lines_processed': 0, - 'events_detected': 0, - 'last_event_time': None, - 'start_time': datetime.now() - } - - async def start(self) -> None: - """Запуск мониторинга""" - if self.running: - logger.warning("Мониторинг уже запущен") - return - - self.running = True - logger.info(f"Запуск мониторинга файла {self.log_path}") - - # Устанавливаем позицию в конец файла при запуске - await self._init_file_position() - - try: - while self.running: - await self._check_log_file() - await asyncio.sleep(self.check_interval) - except Exception as e: - logger.error(f"Ошибка в цикле мониторинга: {e}") - finally: - self.running = False - - async def stop(self) -> None: - """Остановка мониторинга""" - logger.info("Остановка мониторинга") - self.running = False - - async def _init_file_position(self) -> None: - """Инициализация позиции в файле""" - try: - if Path(self.log_path).exists(): - stat = Path(self.log_path).stat() - self.file_position = stat.st_size - self.last_inode = stat.st_ino - logger.info(f"Начальная позиция в файле: {self.file_position}") - else: - logger.warning(f"Лог файл {self.log_path} не найден") - self.file_position = 0 - self.last_inode = None - except Exception as e: - logger.error(f"Ошибка инициализации позиции файла: {e}") - self.file_position = 0 - self.last_inode = None - - async def _check_log_file(self) -> None: - """Проверка изменений в лог файле""" - try: - if not Path(self.log_path).exists(): - logger.warning(f"Лог файл {self.log_path} не существует") - return - - stat = Path(self.log_path).stat() - current_inode = stat.st_ino - current_size = stat.st_size - - # Проверяем, не был ли файл ротирован - if self.last_inode is not None and current_inode != self.last_inode: - logger.info("Обнаружена ротация лог файла") - self.file_position = 0 - self.last_inode = current_inode - - # Проверяем, есть ли новые данные - if current_size > self.file_position: - await self._process_new_lines(current_size) - elif current_size < self.file_position: - # Файл был усечен - logger.info("Файл был усечен, сброс позиции") - self.file_position = 0 - await self._process_new_lines(current_size) - - self.last_inode = current_inode - - except Exception as e: - logger.error(f"Ошибка проверки лог файла: {e}") - - async def _process_new_lines(self, current_size: int) -> None: - """Обработка новых строк в файле""" - try: - async with aiofiles.open(self.log_path, 'r', encoding='utf-8', errors='ignore') as file: - await file.seek(self.file_position) - - while True: - line = await file.readline() - if not line: - break - - self.stats['lines_processed'] += 1 - - # Парсим строку - event = self.parser.parse_line(line) - if event: - self.stats['events_detected'] += 1 - self.stats['last_event_time'] = event.timestamp - - logger.debug(f"Обнаружено событие: {event.event_type} from {event.ip_address}") - - # Отправляем событие в callback - if self.event_callback: - try: - if asyncio.iscoroutinefunction(self.event_callback): - await self.event_callback(event) - else: - self.event_callback(event) - except Exception as e: - logger.error(f"Ошибка в callback: {e}") - - # Обновляем позицию - self.file_position = await file.tell() - - except Exception as e: - logger.error(f"Ошибка обработки новых строк: {e}") - - def get_stats(self) -> Dict: - """Получение статистики мониторинга""" - uptime = datetime.now() - self.stats['start_time'] - - return { - 'running': self.running, - 'log_path': self.log_path, - 'file_position': self.file_position, - 'lines_processed': self.stats['lines_processed'], - 'events_detected': self.stats['events_detected'], - 'last_event_time': self.stats['last_event_time'], - 'uptime_seconds': int(uptime.total_seconds()), - 'check_interval': self.check_interval - } - - async def test_patterns(self, test_lines: List[str]) -> List[LogEvent]: - """Тестирование паттернов на примерах строк""" - events = [] - for line in test_lines: - event = self.parser.parse_line(line) - if event: - events.append(event) - return events - - -class AttackDetector: - """Детектор атак на основе событий""" - - def __init__(self, storage, firewall_manager, security_manager, config: Dict): - self.storage = storage - self.firewall_manager = firewall_manager - self.security_manager = security_manager - self.config = config - - self.max_attempts = config.get('max_attempts', 5) - self.time_window = config.get('time_window', 60) - self.unban_time = config.get('unban_time', 3600) - self.whitelist = config.get('whitelist', []) - - # Callback для уведомлений - self.ban_callback: Optional[Callable] = None - self.unban_callback: Optional[Callable] = None - - def set_callbacks(self, ban_callback: Optional[Callable] = None, - unban_callback: Optional[Callable] = None) -> None: - """Установка callback для уведомлений""" - self.ban_callback = ban_callback - self.unban_callback = unban_callback - - async def process_event(self, event: LogEvent) -> None: - """Обработка события из лога""" - try: - # Передаем событие в SecurityManager для глубокого анализа - await self.security_manager.analyze_login_event(event) - - # Добавляем событие в базу данных - if event.is_success: - await self.storage.add_successful_login( - event.ip_address, - event.username or "unknown", - f"login_type:{event.event_type}" - ) - logger.info(f"Успешный вход: {event.username}@{event.ip_address}") - else: - await self.storage.add_attack_attempt( - event.ip_address, - event.username or "unknown", - event.event_type, - event.log_line, - event.timestamp - ) - - # Проверяем, нужно ли банить IP (стандартная логика брутфорса) - await self._check_and_ban_ip(event.ip_address) - - except Exception as e: - logger.error(f"Ошибка обработки события: {e}") - - async def _check_and_ban_ip(self, ip: str) -> None: - """Проверка и бан IP при превышении лимита""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.info(f"IP {ip} в белом списке, пропускаем") - return - - # Проверяем, не забанен ли уже - if await self.storage.is_ip_banned(ip): - logger.debug(f"IP {ip} уже забанен") - return - - # Получаем количество попыток за время окна - attempts = await self.storage.get_attack_count_for_ip(ip, self.time_window) - - if attempts >= self.max_attempts: - # Баним IP - reason = f"Превышен лимит попыток: {attempts}/{self.max_attempts} за {self.time_window}с" - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=False, attempts_count=attempts - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.warning(f"IP {ip} забанен: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': attempts, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в ban callback: {e}") - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - else: - logger.error(f"Не удалось записать бан IP {ip} в базу данных") - - except Exception as e: - logger.error(f"Ошибка проверки IP {ip} для бана: {e}") - - async def process_unban(self, ip: str) -> bool: - """Разбан IP адреса""" - try: - # Разбаниваем в базе данных - db_success = await self.storage.unban_ip(ip) - - # Разбаниваем в firewall - firewall_success = await self.firewall_manager.unban_ip(ip) - - if db_success and firewall_success: - logger.info(f"IP {ip} успешно разбанен") - - # Уведомление через callback - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в unban callback: {e}") - - return True - else: - logger.error(f"Ошибка разбана IP {ip}") - return False - - except Exception as e: - logger.error(f"Ошибка разбана IP {ip}: {e}") - return False - - async def check_expired_bans(self) -> None: - """Проверка и автоматический разбан истекших IP""" - try: - expired_ips = await self.storage.get_expired_bans() - - for ip in expired_ips: - success = await self.process_unban(ip) - if success: - logger.info(f"IP {ip} автоматически разбанен (истек срок)") - - # Уведомление об автоматическом разбане - if self.unban_callback: - unban_info = { - 'ip': ip, - 'auto': True - } - try: - if asyncio.iscoroutinefunction(self.unban_callback): - await self.unban_callback(unban_info) - else: - self.unban_callback(unban_info) - except Exception as e: - logger.error(f"Ошибка в auto unban callback: {e}") - - except Exception as e: - logger.error(f"Ошибка проверки истекших банов: {e}") - - async def manual_ban(self, ip: str, reason: str = "Ручная блокировка") -> bool: - """Ручной бан IP адреса""" - try: - # Проверяем белый список - if await self.storage.is_whitelisted(ip, self.whitelist): - logger.warning(f"Попытка заблокировать IP {ip} из белого списка") - return False - - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.unban_time, manual=True - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.info(f"IP {ip} ручной бан: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': 0, - 'auto': False - } - try: - if asyncio.iscoroutinefunction(self.ban_callback): - await self.ban_callback(ban_info) - else: - self.ban_callback(ban_info) - except Exception as e: - logger.error(f"Ошибка в manual ban callback: {e}") - - return True - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - return False - else: - logger.error(f"Не удалось записать ручной бан IP {ip} в базу данных") - return False - - except Exception as e: - logger.error(f"Ошибка ручного бана IP {ip}: {e}") - return False \ No newline at end of file diff --git a/.history/src/password_utils_20251125195955.py b/.history/src/password_utils_20251125195955.py deleted file mode 100644 index 11b9c3d..0000000 --- a/.history/src/password_utils_20251125195955.py +++ /dev/null @@ -1,449 +0,0 @@ -""" -Password utilities для PyGuardian -Утилиты для управления паролями пользователей -""" - -import asyncio -import logging -import secrets -import string -import hashlib -import os -import json -from datetime import datetime -from typing import Dict, List, Optional -from cryptography.fernet import Fernet -import crypt - -logger = logging.getLogger(__name__) - - -class PasswordManager: - """Менеджер паролей пользователей""" - - def __init__(self, config: Dict): - self.config = config - - # Параметры генерации паролей - self.default_length = config.get('password_length', 16) - self.use_special_chars = config.get('use_special_chars', True) - self.password_history_size = config.get('password_history_size', 5) - - # Пути к файлам - self.passwords_file = "/var/lib/pyguardian/passwords.json" - self.key_file = "/var/lib/pyguardian/password_encryption.key" - - # Инициализация шифрования - self.encryption_key = self._get_or_create_key() - self.cipher = Fernet(self.encryption_key) - - # Создаем директории если не существуют - os.makedirs(os.path.dirname(self.passwords_file), exist_ok=True) - - def _get_or_create_key(self) -> bytes: - """Получить или создать ключ шифрования для паролей""" - try: - with open(self.key_file, 'rb') as f: - return f.read() - except FileNotFoundError: - # Создаем новый ключ - key = Fernet.generate_key() - with open(self.key_file, 'wb') as f: - f.write(key) - os.chmod(self.key_file, 0o600) # Только root может читать - logger.info("Создан новый ключ шифрования паролей") - return key - - def generate_password(self, - length: Optional[int] = None, - use_special: Optional[bool] = None, - exclude_ambiguous: bool = True) -> str: - """Генерация криптостойкого пароля""" - if length is None: - length = self.default_length - if use_special is None: - use_special = self.use_special_chars - - # Базовый алфавит - lowercase = string.ascii_lowercase - uppercase = string.ascii_uppercase - digits = string.digits - - # Исключаем неоднозначные символы если нужно - if exclude_ambiguous: - lowercase = lowercase.replace('l', '').replace('o', '') - uppercase = uppercase.replace('I', '').replace('O') - digits = digits.replace('0', '').replace('1') - - # Специальные символы - if use_special: - special = "!@#$%^&*" - else: - special = "" - - # Обеспечиваем наличие всех типов символов - password_chars = [] - - # Гарантируем минимум по одному символу каждого типа - password_chars.append(secrets.choice(lowercase)) - password_chars.append(secrets.choice(uppercase)) - password_chars.append(secrets.choice(digits)) - - if use_special and special: - password_chars.append(secrets.choice(special)) - - # Создаем полный алфавит для оставшихся символов - alphabet = lowercase + uppercase + digits + special - - # Добавляем оставшиеся символы - remaining_length = length - len(password_chars) - for _ in range(remaining_length): - password_chars.append(secrets.choice(alphabet)) - - # Перемешиваем массив - secrets.SystemRandom().shuffle(password_chars) - - return ''.join(password_chars) - - def validate_password_strength(self, password: str) -> Dict: - """Проверка силы пароля""" - score = 0 - feedback = [] - - # Длина - if len(password) >= 12: - score += 2 - elif len(password) >= 8: - score += 1 - else: - feedback.append("Пароль слишком короткий (минимум 8 символов)") - - # Наличие разных типов символов - has_lower = any(c.islower() for c in password) - has_upper = any(c.isupper() for c in password) - has_digit = any(c.isdigit() for c in password) - has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password) - - char_types = sum([has_lower, has_upper, has_digit, has_special]) - score += char_types - - if not has_lower: - feedback.append("Добавьте строчные буквы") - if not has_upper: - feedback.append("Добавьте заглавные буквы") - if not has_digit: - feedback.append("Добавьте цифры") - if not has_special: - feedback.append("Добавьте специальные символы") - - # Проверка на повторяющиеся символы - if len(set(password)) < len(password) * 0.7: - score -= 1 - feedback.append("Слишком много повторяющихся символов") - - # Проверка на последовательности - sequences = ["123", "abc", "qwe", "asd", "zxc"] - for seq in sequences: - if seq in password.lower(): - score -= 1 - feedback.append("Избегайте простых последовательностей") - break - - # Итоговая оценка - if score >= 7: - strength = "very_strong" - elif score >= 5: - strength = "strong" - elif score >= 3: - strength = "medium" - elif score >= 1: - strength = "weak" - else: - strength = "very_weak" - - return { - 'score': score, - 'strength': strength, - 'feedback': feedback - } - - async def change_user_password(self, username: str, new_password: str) -> bool: - """Смена пароля пользователя""" - try: - # Проверяем существование пользователя - if not await self._user_exists(username): - logger.error(f"Пользователь {username} не существует") - return False - - # Создаем хеш пароля для системы - salt = crypt.mksalt(crypt.METHOD_SHA512) - hashed_password = crypt.crypt(new_password, salt) - - # Меняем пароль через usermod - process = await asyncio.create_subprocess_exec( - 'usermod', '-p', hashed_password, username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - # Сохраняем информацию о смене пароля - await self._save_password_change(username, new_password, "manual_change") - logger.info(f"✅ Пароль пользователя {username} успешно изменен") - return True - else: - error = stderr.decode() if stderr else "Unknown error" - logger.error(f"❌ Ошибка смены пароля для {username}: {error}") - return False - - except Exception as e: - logger.error(f"Исключение при смене пароля для {username}: {e}") - return False - - async def _user_exists(self, username: str) -> bool: - """Проверка существования пользователя""" - try: - process = await asyncio.create_subprocess_exec( - 'id', username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - return process.returncode == 0 - except Exception: - return False - - async def _save_password_change(self, username: str, password: str, reason: str) -> None: - """Сохранение информации о смене пароля""" - try: - # Загружаем существующие записи - password_data = await self._load_password_data() - - if username not in password_data: - password_data[username] = {'history': []} - - # Шифруем пароль - encrypted_password = self.cipher.encrypt(password.encode()).decode() - - # Добавляем новую запись - change_record = { - 'password': encrypted_password, - 'changed_at': datetime.now().isoformat(), - 'reason': reason, - 'strength': self.validate_password_strength(password) - } - - password_data[username]['history'].insert(0, change_record) - password_data[username]['current'] = change_record - - # Ограничиваем историю - if len(password_data[username]['history']) > self.password_history_size: - password_data[username]['history'] = password_data[username]['history'][:self.password_history_size] - - # Сохраняем - await self._save_password_data(password_data) - - except Exception as e: - logger.error(f"Ошибка сохранения данных о пароле для {username}: {e}") - - async def _load_password_data(self) -> Dict: - """Загрузка данных о паролях""" - try: - if os.path.exists(self.passwords_file): - with open(self.passwords_file, 'r') as f: - return json.load(f) - return {} - except Exception as e: - logger.error(f"Ошибка загрузки данных о паролях: {e}") - return {} - - async def _save_password_data(self, data: Dict) -> None: - """Сохранение данных о паролях""" - try: - with open(self.passwords_file, 'w') as f: - json.dump(data, f, indent=2) - os.chmod(self.passwords_file, 0o600) - except Exception as e: - logger.error(f"Ошибка сохранения данных о паролях: {e}") - - async def get_current_password(self, username: str) -> Optional[str]: - """Получение текущего пароля пользователя""" - try: - password_data = await self._load_password_data() - - if username in password_data and 'current' in password_data[username]: - encrypted = password_data[username]['current']['password'].encode() - return self.cipher.decrypt(encrypted).decode() - - return None - - except Exception as e: - logger.error(f"Ошибка получения пароля для {username}: {e}") - return None - - async def get_password_history(self, username: str) -> List[Dict]: - """Получение истории смены паролей""" - try: - password_data = await self._load_password_data() - - if username in password_data: - history = [] - for record in password_data[username].get('history', []): - # Возвращаем историю без самих паролей (только метаданные) - history.append({ - 'changed_at': record['changed_at'], - 'reason': record['reason'], - 'strength': record.get('strength', {}) - }) - return history - - return [] - - except Exception as e: - logger.error(f"Ошибка получения истории паролей для {username}: {e}") - return [] - - async def generate_and_set_password(self, username: str, reason: str = "automatic_generation") -> Optional[str]: - """Генерация и установка нового пароля""" - try: - # Генерируем новый пароль - new_password = self.generate_password() - - # Устанавливаем пароль - success = await self.change_user_password(username, new_password) - - if success: - logger.info(f"🔑 Сгенерирован и установлен новый пароль для {username}") - return new_password - else: - logger.error(f"❌ Не удалось установить сгенерированный пароль для {username}") - return None - - except Exception as e: - logger.error(f"Ошибка генерации и установки пароля для {username}: {e}") - return None - - async def check_password_age(self, username: str) -> Optional[int]: - """Проверка возраста текущего пароля в днях""" - try: - password_data = await self._load_password_data() - - if username in password_data and 'current' in password_data[username]: - changed_at_str = password_data[username]['current']['changed_at'] - changed_at = datetime.fromisoformat(changed_at_str) - age = (datetime.now() - changed_at).days - return age - - return None - - except Exception as e: - logger.error(f"Ошибка проверки возраста пароля для {username}: {e}") - return None - - async def get_users_with_old_passwords(self, max_age_days: int = 90) -> List[Dict]: - """Получение пользователей с устаревшими паролями""" - try: - password_data = await self._load_password_data() - old_passwords = [] - - for username, data in password_data.items(): - if 'current' in data: - changed_at_str = data['current']['changed_at'] - changed_at = datetime.fromisoformat(changed_at_str) - age_days = (datetime.now() - changed_at).days - - if age_days > max_age_days: - old_passwords.append({ - 'username': username, - 'age_days': age_days, - 'changed_at': changed_at_str, - 'reason': data['current'].get('reason', 'unknown') - }) - - return sorted(old_passwords, key=lambda x: x['age_days'], reverse=True) - - except Exception as e: - logger.error(f"Ошибка получения пользователей с устаревшими паролями: {e}") - return [] - - async def emergency_password_reset(self, username: str) -> Optional[str]: - """Экстренный сброс пароля (при компрометации)""" - try: - # Генерируем особо сложный пароль для экстренного случая - emergency_password = self.generate_password( - length=20, - use_special=True, - exclude_ambiguous=True - ) - - # Устанавливаем пароль - success = await self.change_user_password(username, emergency_password) - - if success: - logger.critical(f"🚨 Экстренный сброс пароля выполнен для {username}") - return emergency_password - else: - logger.error(f"❌ Не удалось выполнить экстренный сброс пароля для {username}") - return None - - except Exception as e: - logger.error(f"Ошибка экстренного сброса пароля для {username}: {e}") - return None - - def get_password_policy(self) -> Dict: - """Получение текущей политики паролей""" - return { - 'min_length': 8, - 'recommended_length': self.default_length, - 'require_uppercase': True, - 'require_lowercase': True, - 'require_digits': True, - 'require_special': self.use_special_chars, - 'max_age_days': 90, - 'history_size': self.password_history_size, - 'exclude_ambiguous': True - } - - async def validate_current_passwords(self) -> List[Dict]: - """Валидация всех текущих паролей на соответствие политике""" - try: - password_data = await self._load_password_data() - validation_results = [] - - for username, data in password_data.items(): - if 'current' in data: - try: - # Расшифровываем пароль для проверки - encrypted = data['current']['password'].encode() - password = self.cipher.decrypt(encrypted).decode() - - # Проверяем силу - strength = self.validate_password_strength(password) - - # Проверяем возраст - age_days = await self.check_password_age(username) - - validation_results.append({ - 'username': username, - 'strength': strength['strength'], - 'score': strength['score'], - 'age_days': age_days, - 'feedback': strength['feedback'], - 'needs_change': ( - strength['strength'] in ['weak', 'very_weak'] or - (age_days and age_days > 90) - ) - }) - - except Exception as e: - validation_results.append({ - 'username': username, - 'error': f"Ошибка валидации: {str(e)}" - }) - - return validation_results - - except Exception as e: - logger.error(f"Ошибка валидации паролей: {e}") - return [] \ No newline at end of file diff --git a/.history/src/password_utils_20251125202055.py b/.history/src/password_utils_20251125202055.py deleted file mode 100644 index 11b9c3d..0000000 --- a/.history/src/password_utils_20251125202055.py +++ /dev/null @@ -1,449 +0,0 @@ -""" -Password utilities для PyGuardian -Утилиты для управления паролями пользователей -""" - -import asyncio -import logging -import secrets -import string -import hashlib -import os -import json -from datetime import datetime -from typing import Dict, List, Optional -from cryptography.fernet import Fernet -import crypt - -logger = logging.getLogger(__name__) - - -class PasswordManager: - """Менеджер паролей пользователей""" - - def __init__(self, config: Dict): - self.config = config - - # Параметры генерации паролей - self.default_length = config.get('password_length', 16) - self.use_special_chars = config.get('use_special_chars', True) - self.password_history_size = config.get('password_history_size', 5) - - # Пути к файлам - self.passwords_file = "/var/lib/pyguardian/passwords.json" - self.key_file = "/var/lib/pyguardian/password_encryption.key" - - # Инициализация шифрования - self.encryption_key = self._get_or_create_key() - self.cipher = Fernet(self.encryption_key) - - # Создаем директории если не существуют - os.makedirs(os.path.dirname(self.passwords_file), exist_ok=True) - - def _get_or_create_key(self) -> bytes: - """Получить или создать ключ шифрования для паролей""" - try: - with open(self.key_file, 'rb') as f: - return f.read() - except FileNotFoundError: - # Создаем новый ключ - key = Fernet.generate_key() - with open(self.key_file, 'wb') as f: - f.write(key) - os.chmod(self.key_file, 0o600) # Только root может читать - logger.info("Создан новый ключ шифрования паролей") - return key - - def generate_password(self, - length: Optional[int] = None, - use_special: Optional[bool] = None, - exclude_ambiguous: bool = True) -> str: - """Генерация криптостойкого пароля""" - if length is None: - length = self.default_length - if use_special is None: - use_special = self.use_special_chars - - # Базовый алфавит - lowercase = string.ascii_lowercase - uppercase = string.ascii_uppercase - digits = string.digits - - # Исключаем неоднозначные символы если нужно - if exclude_ambiguous: - lowercase = lowercase.replace('l', '').replace('o', '') - uppercase = uppercase.replace('I', '').replace('O') - digits = digits.replace('0', '').replace('1') - - # Специальные символы - if use_special: - special = "!@#$%^&*" - else: - special = "" - - # Обеспечиваем наличие всех типов символов - password_chars = [] - - # Гарантируем минимум по одному символу каждого типа - password_chars.append(secrets.choice(lowercase)) - password_chars.append(secrets.choice(uppercase)) - password_chars.append(secrets.choice(digits)) - - if use_special and special: - password_chars.append(secrets.choice(special)) - - # Создаем полный алфавит для оставшихся символов - alphabet = lowercase + uppercase + digits + special - - # Добавляем оставшиеся символы - remaining_length = length - len(password_chars) - for _ in range(remaining_length): - password_chars.append(secrets.choice(alphabet)) - - # Перемешиваем массив - secrets.SystemRandom().shuffle(password_chars) - - return ''.join(password_chars) - - def validate_password_strength(self, password: str) -> Dict: - """Проверка силы пароля""" - score = 0 - feedback = [] - - # Длина - if len(password) >= 12: - score += 2 - elif len(password) >= 8: - score += 1 - else: - feedback.append("Пароль слишком короткий (минимум 8 символов)") - - # Наличие разных типов символов - has_lower = any(c.islower() for c in password) - has_upper = any(c.isupper() for c in password) - has_digit = any(c.isdigit() for c in password) - has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password) - - char_types = sum([has_lower, has_upper, has_digit, has_special]) - score += char_types - - if not has_lower: - feedback.append("Добавьте строчные буквы") - if not has_upper: - feedback.append("Добавьте заглавные буквы") - if not has_digit: - feedback.append("Добавьте цифры") - if not has_special: - feedback.append("Добавьте специальные символы") - - # Проверка на повторяющиеся символы - if len(set(password)) < len(password) * 0.7: - score -= 1 - feedback.append("Слишком много повторяющихся символов") - - # Проверка на последовательности - sequences = ["123", "abc", "qwe", "asd", "zxc"] - for seq in sequences: - if seq in password.lower(): - score -= 1 - feedback.append("Избегайте простых последовательностей") - break - - # Итоговая оценка - if score >= 7: - strength = "very_strong" - elif score >= 5: - strength = "strong" - elif score >= 3: - strength = "medium" - elif score >= 1: - strength = "weak" - else: - strength = "very_weak" - - return { - 'score': score, - 'strength': strength, - 'feedback': feedback - } - - async def change_user_password(self, username: str, new_password: str) -> bool: - """Смена пароля пользователя""" - try: - # Проверяем существование пользователя - if not await self._user_exists(username): - logger.error(f"Пользователь {username} не существует") - return False - - # Создаем хеш пароля для системы - salt = crypt.mksalt(crypt.METHOD_SHA512) - hashed_password = crypt.crypt(new_password, salt) - - # Меняем пароль через usermod - process = await asyncio.create_subprocess_exec( - 'usermod', '-p', hashed_password, username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - # Сохраняем информацию о смене пароля - await self._save_password_change(username, new_password, "manual_change") - logger.info(f"✅ Пароль пользователя {username} успешно изменен") - return True - else: - error = stderr.decode() if stderr else "Unknown error" - logger.error(f"❌ Ошибка смены пароля для {username}: {error}") - return False - - except Exception as e: - logger.error(f"Исключение при смене пароля для {username}: {e}") - return False - - async def _user_exists(self, username: str) -> bool: - """Проверка существования пользователя""" - try: - process = await asyncio.create_subprocess_exec( - 'id', username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - return process.returncode == 0 - except Exception: - return False - - async def _save_password_change(self, username: str, password: str, reason: str) -> None: - """Сохранение информации о смене пароля""" - try: - # Загружаем существующие записи - password_data = await self._load_password_data() - - if username not in password_data: - password_data[username] = {'history': []} - - # Шифруем пароль - encrypted_password = self.cipher.encrypt(password.encode()).decode() - - # Добавляем новую запись - change_record = { - 'password': encrypted_password, - 'changed_at': datetime.now().isoformat(), - 'reason': reason, - 'strength': self.validate_password_strength(password) - } - - password_data[username]['history'].insert(0, change_record) - password_data[username]['current'] = change_record - - # Ограничиваем историю - if len(password_data[username]['history']) > self.password_history_size: - password_data[username]['history'] = password_data[username]['history'][:self.password_history_size] - - # Сохраняем - await self._save_password_data(password_data) - - except Exception as e: - logger.error(f"Ошибка сохранения данных о пароле для {username}: {e}") - - async def _load_password_data(self) -> Dict: - """Загрузка данных о паролях""" - try: - if os.path.exists(self.passwords_file): - with open(self.passwords_file, 'r') as f: - return json.load(f) - return {} - except Exception as e: - logger.error(f"Ошибка загрузки данных о паролях: {e}") - return {} - - async def _save_password_data(self, data: Dict) -> None: - """Сохранение данных о паролях""" - try: - with open(self.passwords_file, 'w') as f: - json.dump(data, f, indent=2) - os.chmod(self.passwords_file, 0o600) - except Exception as e: - logger.error(f"Ошибка сохранения данных о паролях: {e}") - - async def get_current_password(self, username: str) -> Optional[str]: - """Получение текущего пароля пользователя""" - try: - password_data = await self._load_password_data() - - if username in password_data and 'current' in password_data[username]: - encrypted = password_data[username]['current']['password'].encode() - return self.cipher.decrypt(encrypted).decode() - - return None - - except Exception as e: - logger.error(f"Ошибка получения пароля для {username}: {e}") - return None - - async def get_password_history(self, username: str) -> List[Dict]: - """Получение истории смены паролей""" - try: - password_data = await self._load_password_data() - - if username in password_data: - history = [] - for record in password_data[username].get('history', []): - # Возвращаем историю без самих паролей (только метаданные) - history.append({ - 'changed_at': record['changed_at'], - 'reason': record['reason'], - 'strength': record.get('strength', {}) - }) - return history - - return [] - - except Exception as e: - logger.error(f"Ошибка получения истории паролей для {username}: {e}") - return [] - - async def generate_and_set_password(self, username: str, reason: str = "automatic_generation") -> Optional[str]: - """Генерация и установка нового пароля""" - try: - # Генерируем новый пароль - new_password = self.generate_password() - - # Устанавливаем пароль - success = await self.change_user_password(username, new_password) - - if success: - logger.info(f"🔑 Сгенерирован и установлен новый пароль для {username}") - return new_password - else: - logger.error(f"❌ Не удалось установить сгенерированный пароль для {username}") - return None - - except Exception as e: - logger.error(f"Ошибка генерации и установки пароля для {username}: {e}") - return None - - async def check_password_age(self, username: str) -> Optional[int]: - """Проверка возраста текущего пароля в днях""" - try: - password_data = await self._load_password_data() - - if username in password_data and 'current' in password_data[username]: - changed_at_str = password_data[username]['current']['changed_at'] - changed_at = datetime.fromisoformat(changed_at_str) - age = (datetime.now() - changed_at).days - return age - - return None - - except Exception as e: - logger.error(f"Ошибка проверки возраста пароля для {username}: {e}") - return None - - async def get_users_with_old_passwords(self, max_age_days: int = 90) -> List[Dict]: - """Получение пользователей с устаревшими паролями""" - try: - password_data = await self._load_password_data() - old_passwords = [] - - for username, data in password_data.items(): - if 'current' in data: - changed_at_str = data['current']['changed_at'] - changed_at = datetime.fromisoformat(changed_at_str) - age_days = (datetime.now() - changed_at).days - - if age_days > max_age_days: - old_passwords.append({ - 'username': username, - 'age_days': age_days, - 'changed_at': changed_at_str, - 'reason': data['current'].get('reason', 'unknown') - }) - - return sorted(old_passwords, key=lambda x: x['age_days'], reverse=True) - - except Exception as e: - logger.error(f"Ошибка получения пользователей с устаревшими паролями: {e}") - return [] - - async def emergency_password_reset(self, username: str) -> Optional[str]: - """Экстренный сброс пароля (при компрометации)""" - try: - # Генерируем особо сложный пароль для экстренного случая - emergency_password = self.generate_password( - length=20, - use_special=True, - exclude_ambiguous=True - ) - - # Устанавливаем пароль - success = await self.change_user_password(username, emergency_password) - - if success: - logger.critical(f"🚨 Экстренный сброс пароля выполнен для {username}") - return emergency_password - else: - logger.error(f"❌ Не удалось выполнить экстренный сброс пароля для {username}") - return None - - except Exception as e: - logger.error(f"Ошибка экстренного сброса пароля для {username}: {e}") - return None - - def get_password_policy(self) -> Dict: - """Получение текущей политики паролей""" - return { - 'min_length': 8, - 'recommended_length': self.default_length, - 'require_uppercase': True, - 'require_lowercase': True, - 'require_digits': True, - 'require_special': self.use_special_chars, - 'max_age_days': 90, - 'history_size': self.password_history_size, - 'exclude_ambiguous': True - } - - async def validate_current_passwords(self) -> List[Dict]: - """Валидация всех текущих паролей на соответствие политике""" - try: - password_data = await self._load_password_data() - validation_results = [] - - for username, data in password_data.items(): - if 'current' in data: - try: - # Расшифровываем пароль для проверки - encrypted = data['current']['password'].encode() - password = self.cipher.decrypt(encrypted).decode() - - # Проверяем силу - strength = self.validate_password_strength(password) - - # Проверяем возраст - age_days = await self.check_password_age(username) - - validation_results.append({ - 'username': username, - 'strength': strength['strength'], - 'score': strength['score'], - 'age_days': age_days, - 'feedback': strength['feedback'], - 'needs_change': ( - strength['strength'] in ['weak', 'very_weak'] or - (age_days and age_days > 90) - ) - }) - - except Exception as e: - validation_results.append({ - 'username': username, - 'error': f"Ошибка валидации: {str(e)}" - }) - - return validation_results - - except Exception as e: - logger.error(f"Ошибка валидации паролей: {e}") - return [] \ No newline at end of file diff --git a/.history/src/security_20251125195757.py b/.history/src/security_20251125195757.py deleted file mode 100644 index 64b9588..0000000 --- a/.history/src/security_20251125195757.py +++ /dev/null @@ -1,516 +0,0 @@ -""" -Security module для PyGuardian -Основная логика обнаружения угроз и скрытого реагирования на взломы -""" - -import asyncio -import logging -import secrets -import string -import subprocess -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from cryptography.fernet import Fernet -import base64 -import json - -logger = logging.getLogger(__name__) - - -class SecurityManager: - """Менеджер безопасности - ключевой компонент системы""" - - def __init__(self, storage, firewall_manager, config: Dict): - self.storage = storage - self.firewall_manager = firewall_manager - self.config = config - - # Параметры безопасности - self.max_attempts = config.get('max_attempts', 5) - self.time_window = config.get('time_window', 60) - self.whitelist = config.get('whitelist', []) - - # Параметры для детекции взломов - self.authorized_users = config.get('authorized_users', []) - self.honeypot_users = config.get('honeypot_users', []) - self.stealth_mode_duration = config.get('stealth_mode_duration', 300) # 5 минут - - # Шифрование для паролей - self.encryption_key = self._get_or_create_key() - self.cipher = Fernet(self.encryption_key) - - # Callbacks - self.compromise_callback: Optional[Callable] = None - self.ban_callback: Optional[Callable] = None - - def _get_or_create_key(self) -> bytes: - """Получить или создать ключ шифрования""" - key_file = "/var/lib/pyguardian/encryption.key" - try: - with open(key_file, 'rb') as f: - return f.read() - except FileNotFoundError: - # Создаем новый ключ - import os - os.makedirs(os.path.dirname(key_file), exist_ok=True) - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) # Только root может читать - return key - - def set_callbacks(self, compromise_callback: Optional[Callable] = None, - ban_callback: Optional[Callable] = None) -> None: - """Установка callbacks для уведомлений""" - self.compromise_callback = compromise_callback - self.ban_callback = ban_callback - - async def analyze_login_event(self, event) -> None: - """Анализ события входа в систему""" - try: - if event.is_success: - await self._handle_successful_login(event) - else: - await self._handle_failed_login(event) - except Exception as e: - logger.error(f"Ошибка анализа события входа: {e}") - - async def _handle_failed_login(self, event) -> None: - """Обработка неудачного входа""" - # Проверяем белый список - if await self.storage.is_whitelisted(event.ip_address, self.whitelist): - return - - # Получаем количество попыток - attempts = await self.storage.get_attack_count_for_ip(event.ip_address, self.time_window) - - # Проверяем honeypot пользователей - if event.username in self.honeypot_users: - logger.warning(f"Попытка входа под honeypot пользователем {event.username} от {event.ip_address}") - # Мгновенный бан за попытку honeypot входа - await self._execute_ban(event.ip_address, f"Попытка входа под honeypot пользователем {event.username}") - return - - # Обычная логика брутфорса - if attempts >= self.max_attempts: - await self._execute_ban(event.ip_address, f"Брутфорс атака: {attempts} попыток за {self.time_window}с") - - async def _handle_successful_login(self, event) -> None: - """КРИТИЧЕСКАЯ ФУНКЦИЯ: Обработка УСПЕШНОГО входа (потенциальный взлом)""" - logger.info(f"Анализ успешного входа: {event.username}@{event.ip_address}") - - # Проверяем признаки взлома - is_compromised = await self._detect_compromise(event) - - if is_compromised: - logger.critical(f"🚨 ОБНАРУЖЕН ВЗЛОМ: {event.username}@{event.ip_address}") - await self._handle_compromise(event) - else: - logger.info(f"✅ Легитимный вход: {event.username}@{event.ip_address}") - # Записываем как успешный легитимный вход - await self.storage.add_successful_login( - event.ip_address, - event.username, - "legitimate_login" - ) - - async def _detect_compromise(self, event) -> bool: - """Детекция признаков взлома""" - suspicious_indicators = [] - - # 1. IP был замечен в брутфорс атаках - recent_attempts = await self.storage.get_attack_count_for_ip(event.ip_address, 3600) # За час - if recent_attempts > 0: - suspicious_indicators.append(f"Предыдущие атаки: {recent_attempts}") - - # 2. IP не в белом списке - if not await self.storage.is_whitelisted(event.ip_address, self.whitelist): - suspicious_indicators.append("IP не в белом списке") - - # 3. Пользователь не должен входить извне - if event.username not in self.authorized_users: - suspicious_indicators.append(f"Неавторизованный пользователь: {event.username}") - - # 4. Honeypot пользователь (это точно взлом!) - if event.username in self.honeypot_users: - suspicious_indicators.append(f"HONEYPOT пользователь: {event.username}") - return True # Безусловно взлом - - # 5. Проверяем паттерны времени (например, вход ночью) - current_hour = datetime.now().hour - if current_hour < 6 or current_hour > 23: # Подозрительное время - suspicious_indicators.append(f"Подозрительное время: {current_hour}:xx") - - # 6. Проверяем предыдущие взломы с этого IP - details = await self.storage.get_ip_details(event.ip_address) - if details.get('previous_compromises', 0) > 0: - suspicious_indicators.append("Предыдущие взломы с этого IP") - - logger.info(f"Индикаторы подозрительности для {event.ip_address}: {suspicious_indicators}") - - # Считаем взломом если есть >= 2 индикаторов - return len(suspicious_indicators) >= 2 - - async def _handle_compromise(self, event) -> None: - """🔥 СКРЫТОЕ РЕАГИРОВАНИЕ НА ВЗЛОМ""" - logger.critical(f"Инициация скрытой реакции на взлом {event.username}@{event.ip_address}") - - compromise_info = { - 'ip': event.ip_address, - 'username': event.username, - 'timestamp': event.timestamp, - 'detection_time': datetime.now(), - 'session_active': True - } - - try: - # 1. МГНОВЕННО блокируем IP (скрытно - новые подключения невозможны) - await self._stealth_block_ip(event.ip_address) - - # 2. АВТОМАТИЧЕСКИ меняем пароль пользователя - new_password = await self._change_user_password(event.username) - compromise_info['new_password'] = new_password - - # 3. Записываем инцидент в базу - await self._record_compromise(compromise_info) - - # 4. Получаем информацию об активной сессии - session_info = await self._get_active_sessions(event.username) - compromise_info['sessions'] = session_info - - # 5. Уведомляем администратора через Telegram - if self.compromise_callback: - await self.compromise_callback(compromise_info) - - logger.info("✅ Скрытая реакция на взлом выполнена успешно") - - except Exception as e: - logger.error(f"❌ Ошибка в скрытой реакции на взлом: {e}") - # Даже при ошибке пытаемся уведомить - if self.compromise_callback: - compromise_info['error'] = str(e) - await self.compromise_callback(compromise_info) - - async def _stealth_block_ip(self, ip: str) -> None: - """Скрытная блокировка IP (новые соединения)""" - try: - # Блокируем через firewall - success = await self.firewall_manager.ban_ip(ip) - - if success: - # Записываем в базу как компромисс-бан - await self.storage.ban_ip( - ip, - "🚨 АВТОМАТИЧЕСКИЙ БАН - ОБНАРУЖЕН ВЗЛОМ", - 86400, # 24 часа - manual=False, - attempts_count=999 # Специальный маркер взлома - ) - logger.info(f"🔒 IP {ip} скрытно заблокирован (взлом)") - else: - logger.error(f"❌ Не удалось заблокировать IP {ip}") - - except Exception as e: - logger.error(f"Ошибка скрытной блокировки IP {ip}: {e}") - - async def _change_user_password(self, username: str) -> str: - """Автоматическая смена пароля пользователя""" - try: - # Генерируем криптостойкий пароль - new_password = self.generate_secure_password() - - # Меняем пароль через chpasswd - process = await asyncio.create_subprocess_exec( - 'chpasswd', - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - # Отправляем username:password в chpasswd - password_input = f"{username}:{new_password}\n" - stdout, stderr = await process.communicate(password_input.encode()) - - if process.returncode == 0: - # Сохраняем зашифрованный пароль - encrypted_password = self.cipher.encrypt(new_password.encode()).decode() - await self._store_password(username, encrypted_password) - - logger.info(f"🔑 Пароль пользователя {username} автоматически изменен") - return new_password - else: - error = stderr.decode() if stderr else "Unknown error" - logger.error(f"❌ Ошибка смены пароля для {username}: {error}") - return None - - except Exception as e: - logger.error(f"Ошибка смены пароля для {username}: {e}") - return None - - def generate_secure_password(self, length: int = 16) -> str: - """Генерация криптостойкого пароля""" - # Используем все безопасные символы - alphabet = string.ascii_letters + string.digits + "!@#$%^&*" - - # Обеспечиваем наличие разных типов символов - password = [ - secrets.choice(string.ascii_lowercase), - secrets.choice(string.ascii_uppercase), - secrets.choice(string.digits), - secrets.choice("!@#$%^&*") - ] - - # Добавляем оставшиеся символы - for _ in range(length - 4): - password.append(secrets.choice(alphabet)) - - # Перемешиваем - secrets.SystemRandom().shuffle(password) - return ''.join(password) - - async def _store_password(self, username: str, encrypted_password: str) -> None: - """Сохранение зашифрованного пароля""" - try: - passwords_file = "/var/lib/pyguardian/passwords.json" - - # Загружаем существующие пароли - try: - with open(passwords_file, 'r') as f: - passwords = json.load(f) - except FileNotFoundError: - passwords = {} - - # Добавляем новый пароль с timestamp - passwords[username] = { - 'password': encrypted_password, - 'changed_at': datetime.now().isoformat(), - 'reason': 'compromise_detection' - } - - # Сохраняем - import os - os.makedirs(os.path.dirname(passwords_file), exist_ok=True) - with open(passwords_file, 'w') as f: - json.dump(passwords, f, indent=2) - os.chmod(passwords_file, 0o600) - - except Exception as e: - logger.error(f"Ошибка сохранения пароля для {username}: {e}") - - async def get_stored_password(self, username: str) -> Optional[str]: - """Получение сохраненного пароля""" - try: - passwords_file = "/var/lib/pyguardian/passwords.json" - with open(passwords_file, 'r') as f: - passwords = json.load(f) - - if username in passwords: - encrypted = passwords[username]['password'].encode() - return self.cipher.decrypt(encrypted).decode() - return None - - except Exception as e: - logger.error(f"Ошибка получения пароля для {username}: {e}") - return None - - async def _get_active_sessions(self, username: str = None) -> List[Dict]: - """Получение информации об активных SSH сессиях""" - try: - sessions = [] - - # Используем who для получения активных сессий - process = await asyncio.create_subprocess_exec( - 'who', '-u', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - lines = stdout.decode().strip().split('\n') - for line in lines: - if line.strip(): - parts = line.split() - if len(parts) >= 7: - session_user = parts[0] - tty = parts[1] - login_time = ' '.join(parts[2:6]) - pid = parts[6] - - # Фильтруем по пользователю если указан - if username is None or session_user == username: - sessions.append({ - 'username': session_user, - 'tty': tty, - 'login_time': login_time, - 'pid': pid.strip('()'), - 'type': 'ssh' if 'pts' in tty else 'console' - }) - - return sessions - - except Exception as e: - logger.error(f"Ошибка получения активных сессий: {e}") - return [] - - async def terminate_user_sessions(self, username: str) -> int: - """Завершение всех сессий пользователя""" - try: - # Получаем активные сессии - sessions = await self._get_active_sessions(username) - terminated = 0 - - for session in sessions: - pid = session.get('pid') - if pid and pid.isdigit(): - try: - # Завершаем процесс - process = await asyncio.create_subprocess_exec( - 'kill', '-KILL', pid, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - terminated += 1 - logger.info(f"🔪 Завершена сессия {session['tty']} (PID {pid}) пользователя {username}") - - except Exception as e: - logger.error(f"Ошибка завершения сессии PID {pid}: {e}") - - logger.info(f"Завершено {terminated} сессий пользователя {username}") - return terminated - - except Exception as e: - logger.error(f"Ошибка завершения сессий пользователя {username}: {e}") - return 0 - - async def _record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о взломе в базу""" - try: - # Расширяем таблицу компромиссов в storage - await self.storage.record_compromise(compromise_info) - - except Exception as e: - logger.error(f"Ошибка записи компромисса: {e}") - - async def _execute_ban(self, ip: str, reason: str) -> None: - """Выполнение бана IP""" - try: - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.config.get('unban_time', 3600), - manual=False, attempts_count=0 - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.warning(f"IP {ip} забанен: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': 0, - 'auto': True - } - await self.ban_callback(ban_info) - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - else: - logger.error(f"Не удалось записать бан IP {ip} в базу данных") - - except Exception as e: - logger.error(f"Ошибка выполнения бана IP {ip}: {e}") - - async def manual_password_change(self, username: str, new_password: str = None) -> str: - """Ручная смена пароля через Telegram""" - if new_password is None: - new_password = self.generate_secure_password() - - try: - # Меняем пароль - process = await asyncio.create_subprocess_exec( - 'chpasswd', - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - password_input = f"{username}:{new_password}\n" - stdout, stderr = await process.communicate(password_input.encode()) - - if process.returncode == 0: - # Сохраняем зашифрованный пароль - encrypted_password = self.cipher.encrypt(new_password.encode()).decode() - await self._store_password(username, encrypted_password) - - logger.info(f"🔑 Пароль пользователя {username} изменен вручную") - return new_password - else: - error = stderr.decode() if stderr else "Unknown error" - logger.error(f"❌ Ошибка ручной смены пароля для {username}: {error}") - return None - - except Exception as e: - logger.error(f"Ошибка ручной смены пароля для {username}: {e}") - return None - - -class HoneypotManager: - """Менеджер honeypot пользователей и ловушек""" - - def __init__(self, config: Dict): - self.honeypot_users = config.get('honeypot_users', []) - self.fake_services = config.get('fake_services', {}) - - async def setup_honeypots(self) -> None: - """Настройка honeypot пользователей""" - try: - for user in self.honeypot_users: - await self._create_honeypot_user(user) - - except Exception as e: - logger.error(f"Ошибка настройки honeypot: {e}") - - async def _create_honeypot_user(self, username: str) -> None: - """Создание honeypot пользователя""" - try: - # Проверяем, существует ли пользователь - process = await asyncio.create_subprocess_exec( - 'id', username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode != 0: - # Пользователь не существует, создаем - process = await asyncio.create_subprocess_exec( - 'useradd', '-m', '-s', '/bin/bash', username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - # Устанавливаем слабый пароль для honeypot - weak_password = username # Очень слабый пароль - process = await asyncio.create_subprocess_exec( - 'chpasswd', - stdin=asyncio.subprocess.PIPE - ) - password_input = f"{username}:{weak_password}\n" - await process.communicate(password_input.encode()) - - logger.info(f"🍯 Honeypot пользователь {username} создан") - else: - logger.error(f"Ошибка создания honeypot пользователя {username}") - - except Exception as e: - logger.error(f"Ошибка создания honeypot пользователя {username}: {e}") \ No newline at end of file diff --git a/.history/src/security_20251125202055.py b/.history/src/security_20251125202055.py deleted file mode 100644 index 64b9588..0000000 --- a/.history/src/security_20251125202055.py +++ /dev/null @@ -1,516 +0,0 @@ -""" -Security module для PyGuardian -Основная логика обнаружения угроз и скрытого реагирования на взломы -""" - -import asyncio -import logging -import secrets -import string -import subprocess -from datetime import datetime, timedelta -from typing import Dict, List, Optional, Callable -from cryptography.fernet import Fernet -import base64 -import json - -logger = logging.getLogger(__name__) - - -class SecurityManager: - """Менеджер безопасности - ключевой компонент системы""" - - def __init__(self, storage, firewall_manager, config: Dict): - self.storage = storage - self.firewall_manager = firewall_manager - self.config = config - - # Параметры безопасности - self.max_attempts = config.get('max_attempts', 5) - self.time_window = config.get('time_window', 60) - self.whitelist = config.get('whitelist', []) - - # Параметры для детекции взломов - self.authorized_users = config.get('authorized_users', []) - self.honeypot_users = config.get('honeypot_users', []) - self.stealth_mode_duration = config.get('stealth_mode_duration', 300) # 5 минут - - # Шифрование для паролей - self.encryption_key = self._get_or_create_key() - self.cipher = Fernet(self.encryption_key) - - # Callbacks - self.compromise_callback: Optional[Callable] = None - self.ban_callback: Optional[Callable] = None - - def _get_or_create_key(self) -> bytes: - """Получить или создать ключ шифрования""" - key_file = "/var/lib/pyguardian/encryption.key" - try: - with open(key_file, 'rb') as f: - return f.read() - except FileNotFoundError: - # Создаем новый ключ - import os - os.makedirs(os.path.dirname(key_file), exist_ok=True) - key = Fernet.generate_key() - with open(key_file, 'wb') as f: - f.write(key) - os.chmod(key_file, 0o600) # Только root может читать - return key - - def set_callbacks(self, compromise_callback: Optional[Callable] = None, - ban_callback: Optional[Callable] = None) -> None: - """Установка callbacks для уведомлений""" - self.compromise_callback = compromise_callback - self.ban_callback = ban_callback - - async def analyze_login_event(self, event) -> None: - """Анализ события входа в систему""" - try: - if event.is_success: - await self._handle_successful_login(event) - else: - await self._handle_failed_login(event) - except Exception as e: - logger.error(f"Ошибка анализа события входа: {e}") - - async def _handle_failed_login(self, event) -> None: - """Обработка неудачного входа""" - # Проверяем белый список - if await self.storage.is_whitelisted(event.ip_address, self.whitelist): - return - - # Получаем количество попыток - attempts = await self.storage.get_attack_count_for_ip(event.ip_address, self.time_window) - - # Проверяем honeypot пользователей - if event.username in self.honeypot_users: - logger.warning(f"Попытка входа под honeypot пользователем {event.username} от {event.ip_address}") - # Мгновенный бан за попытку honeypot входа - await self._execute_ban(event.ip_address, f"Попытка входа под honeypot пользователем {event.username}") - return - - # Обычная логика брутфорса - if attempts >= self.max_attempts: - await self._execute_ban(event.ip_address, f"Брутфорс атака: {attempts} попыток за {self.time_window}с") - - async def _handle_successful_login(self, event) -> None: - """КРИТИЧЕСКАЯ ФУНКЦИЯ: Обработка УСПЕШНОГО входа (потенциальный взлом)""" - logger.info(f"Анализ успешного входа: {event.username}@{event.ip_address}") - - # Проверяем признаки взлома - is_compromised = await self._detect_compromise(event) - - if is_compromised: - logger.critical(f"🚨 ОБНАРУЖЕН ВЗЛОМ: {event.username}@{event.ip_address}") - await self._handle_compromise(event) - else: - logger.info(f"✅ Легитимный вход: {event.username}@{event.ip_address}") - # Записываем как успешный легитимный вход - await self.storage.add_successful_login( - event.ip_address, - event.username, - "legitimate_login" - ) - - async def _detect_compromise(self, event) -> bool: - """Детекция признаков взлома""" - suspicious_indicators = [] - - # 1. IP был замечен в брутфорс атаках - recent_attempts = await self.storage.get_attack_count_for_ip(event.ip_address, 3600) # За час - if recent_attempts > 0: - suspicious_indicators.append(f"Предыдущие атаки: {recent_attempts}") - - # 2. IP не в белом списке - if not await self.storage.is_whitelisted(event.ip_address, self.whitelist): - suspicious_indicators.append("IP не в белом списке") - - # 3. Пользователь не должен входить извне - if event.username not in self.authorized_users: - suspicious_indicators.append(f"Неавторизованный пользователь: {event.username}") - - # 4. Honeypot пользователь (это точно взлом!) - if event.username in self.honeypot_users: - suspicious_indicators.append(f"HONEYPOT пользователь: {event.username}") - return True # Безусловно взлом - - # 5. Проверяем паттерны времени (например, вход ночью) - current_hour = datetime.now().hour - if current_hour < 6 or current_hour > 23: # Подозрительное время - suspicious_indicators.append(f"Подозрительное время: {current_hour}:xx") - - # 6. Проверяем предыдущие взломы с этого IP - details = await self.storage.get_ip_details(event.ip_address) - if details.get('previous_compromises', 0) > 0: - suspicious_indicators.append("Предыдущие взломы с этого IP") - - logger.info(f"Индикаторы подозрительности для {event.ip_address}: {suspicious_indicators}") - - # Считаем взломом если есть >= 2 индикаторов - return len(suspicious_indicators) >= 2 - - async def _handle_compromise(self, event) -> None: - """🔥 СКРЫТОЕ РЕАГИРОВАНИЕ НА ВЗЛОМ""" - logger.critical(f"Инициация скрытой реакции на взлом {event.username}@{event.ip_address}") - - compromise_info = { - 'ip': event.ip_address, - 'username': event.username, - 'timestamp': event.timestamp, - 'detection_time': datetime.now(), - 'session_active': True - } - - try: - # 1. МГНОВЕННО блокируем IP (скрытно - новые подключения невозможны) - await self._stealth_block_ip(event.ip_address) - - # 2. АВТОМАТИЧЕСКИ меняем пароль пользователя - new_password = await self._change_user_password(event.username) - compromise_info['new_password'] = new_password - - # 3. Записываем инцидент в базу - await self._record_compromise(compromise_info) - - # 4. Получаем информацию об активной сессии - session_info = await self._get_active_sessions(event.username) - compromise_info['sessions'] = session_info - - # 5. Уведомляем администратора через Telegram - if self.compromise_callback: - await self.compromise_callback(compromise_info) - - logger.info("✅ Скрытая реакция на взлом выполнена успешно") - - except Exception as e: - logger.error(f"❌ Ошибка в скрытой реакции на взлом: {e}") - # Даже при ошибке пытаемся уведомить - if self.compromise_callback: - compromise_info['error'] = str(e) - await self.compromise_callback(compromise_info) - - async def _stealth_block_ip(self, ip: str) -> None: - """Скрытная блокировка IP (новые соединения)""" - try: - # Блокируем через firewall - success = await self.firewall_manager.ban_ip(ip) - - if success: - # Записываем в базу как компромисс-бан - await self.storage.ban_ip( - ip, - "🚨 АВТОМАТИЧЕСКИЙ БАН - ОБНАРУЖЕН ВЗЛОМ", - 86400, # 24 часа - manual=False, - attempts_count=999 # Специальный маркер взлома - ) - logger.info(f"🔒 IP {ip} скрытно заблокирован (взлом)") - else: - logger.error(f"❌ Не удалось заблокировать IP {ip}") - - except Exception as e: - logger.error(f"Ошибка скрытной блокировки IP {ip}: {e}") - - async def _change_user_password(self, username: str) -> str: - """Автоматическая смена пароля пользователя""" - try: - # Генерируем криптостойкий пароль - new_password = self.generate_secure_password() - - # Меняем пароль через chpasswd - process = await asyncio.create_subprocess_exec( - 'chpasswd', - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - # Отправляем username:password в chpasswd - password_input = f"{username}:{new_password}\n" - stdout, stderr = await process.communicate(password_input.encode()) - - if process.returncode == 0: - # Сохраняем зашифрованный пароль - encrypted_password = self.cipher.encrypt(new_password.encode()).decode() - await self._store_password(username, encrypted_password) - - logger.info(f"🔑 Пароль пользователя {username} автоматически изменен") - return new_password - else: - error = stderr.decode() if stderr else "Unknown error" - logger.error(f"❌ Ошибка смены пароля для {username}: {error}") - return None - - except Exception as e: - logger.error(f"Ошибка смены пароля для {username}: {e}") - return None - - def generate_secure_password(self, length: int = 16) -> str: - """Генерация криптостойкого пароля""" - # Используем все безопасные символы - alphabet = string.ascii_letters + string.digits + "!@#$%^&*" - - # Обеспечиваем наличие разных типов символов - password = [ - secrets.choice(string.ascii_lowercase), - secrets.choice(string.ascii_uppercase), - secrets.choice(string.digits), - secrets.choice("!@#$%^&*") - ] - - # Добавляем оставшиеся символы - for _ in range(length - 4): - password.append(secrets.choice(alphabet)) - - # Перемешиваем - secrets.SystemRandom().shuffle(password) - return ''.join(password) - - async def _store_password(self, username: str, encrypted_password: str) -> None: - """Сохранение зашифрованного пароля""" - try: - passwords_file = "/var/lib/pyguardian/passwords.json" - - # Загружаем существующие пароли - try: - with open(passwords_file, 'r') as f: - passwords = json.load(f) - except FileNotFoundError: - passwords = {} - - # Добавляем новый пароль с timestamp - passwords[username] = { - 'password': encrypted_password, - 'changed_at': datetime.now().isoformat(), - 'reason': 'compromise_detection' - } - - # Сохраняем - import os - os.makedirs(os.path.dirname(passwords_file), exist_ok=True) - with open(passwords_file, 'w') as f: - json.dump(passwords, f, indent=2) - os.chmod(passwords_file, 0o600) - - except Exception as e: - logger.error(f"Ошибка сохранения пароля для {username}: {e}") - - async def get_stored_password(self, username: str) -> Optional[str]: - """Получение сохраненного пароля""" - try: - passwords_file = "/var/lib/pyguardian/passwords.json" - with open(passwords_file, 'r') as f: - passwords = json.load(f) - - if username in passwords: - encrypted = passwords[username]['password'].encode() - return self.cipher.decrypt(encrypted).decode() - return None - - except Exception as e: - logger.error(f"Ошибка получения пароля для {username}: {e}") - return None - - async def _get_active_sessions(self, username: str = None) -> List[Dict]: - """Получение информации об активных SSH сессиях""" - try: - sessions = [] - - # Используем who для получения активных сессий - process = await asyncio.create_subprocess_exec( - 'who', '-u', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - lines = stdout.decode().strip().split('\n') - for line in lines: - if line.strip(): - parts = line.split() - if len(parts) >= 7: - session_user = parts[0] - tty = parts[1] - login_time = ' '.join(parts[2:6]) - pid = parts[6] - - # Фильтруем по пользователю если указан - if username is None or session_user == username: - sessions.append({ - 'username': session_user, - 'tty': tty, - 'login_time': login_time, - 'pid': pid.strip('()'), - 'type': 'ssh' if 'pts' in tty else 'console' - }) - - return sessions - - except Exception as e: - logger.error(f"Ошибка получения активных сессий: {e}") - return [] - - async def terminate_user_sessions(self, username: str) -> int: - """Завершение всех сессий пользователя""" - try: - # Получаем активные сессии - sessions = await self._get_active_sessions(username) - terminated = 0 - - for session in sessions: - pid = session.get('pid') - if pid and pid.isdigit(): - try: - # Завершаем процесс - process = await asyncio.create_subprocess_exec( - 'kill', '-KILL', pid, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - terminated += 1 - logger.info(f"🔪 Завершена сессия {session['tty']} (PID {pid}) пользователя {username}") - - except Exception as e: - logger.error(f"Ошибка завершения сессии PID {pid}: {e}") - - logger.info(f"Завершено {terminated} сессий пользователя {username}") - return terminated - - except Exception as e: - logger.error(f"Ошибка завершения сессий пользователя {username}: {e}") - return 0 - - async def _record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о взломе в базу""" - try: - # Расширяем таблицу компромиссов в storage - await self.storage.record_compromise(compromise_info) - - except Exception as e: - logger.error(f"Ошибка записи компромисса: {e}") - - async def _execute_ban(self, ip: str, reason: str) -> None: - """Выполнение бана IP""" - try: - # Записываем в базу данных - success = await self.storage.ban_ip( - ip, reason, self.config.get('unban_time', 3600), - manual=False, attempts_count=0 - ) - - if success: - # Блокируем через firewall - firewall_success = await self.firewall_manager.ban_ip(ip) - - if firewall_success: - logger.warning(f"IP {ip} забанен: {reason}") - - # Уведомление через callback - if self.ban_callback: - ban_info = { - 'ip': ip, - 'reason': reason, - 'attempts': 0, - 'auto': True - } - await self.ban_callback(ban_info) - else: - logger.error(f"Не удалось заблокировать IP {ip} через firewall") - else: - logger.error(f"Не удалось записать бан IP {ip} в базу данных") - - except Exception as e: - logger.error(f"Ошибка выполнения бана IP {ip}: {e}") - - async def manual_password_change(self, username: str, new_password: str = None) -> str: - """Ручная смена пароля через Telegram""" - if new_password is None: - new_password = self.generate_secure_password() - - try: - # Меняем пароль - process = await asyncio.create_subprocess_exec( - 'chpasswd', - stdin=asyncio.subprocess.PIPE, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - - password_input = f"{username}:{new_password}\n" - stdout, stderr = await process.communicate(password_input.encode()) - - if process.returncode == 0: - # Сохраняем зашифрованный пароль - encrypted_password = self.cipher.encrypt(new_password.encode()).decode() - await self._store_password(username, encrypted_password) - - logger.info(f"🔑 Пароль пользователя {username} изменен вручную") - return new_password - else: - error = stderr.decode() if stderr else "Unknown error" - logger.error(f"❌ Ошибка ручной смены пароля для {username}: {error}") - return None - - except Exception as e: - logger.error(f"Ошибка ручной смены пароля для {username}: {e}") - return None - - -class HoneypotManager: - """Менеджер honeypot пользователей и ловушек""" - - def __init__(self, config: Dict): - self.honeypot_users = config.get('honeypot_users', []) - self.fake_services = config.get('fake_services', {}) - - async def setup_honeypots(self) -> None: - """Настройка honeypot пользователей""" - try: - for user in self.honeypot_users: - await self._create_honeypot_user(user) - - except Exception as e: - logger.error(f"Ошибка настройки honeypot: {e}") - - async def _create_honeypot_user(self, username: str) -> None: - """Создание honeypot пользователя""" - try: - # Проверяем, существует ли пользователь - process = await asyncio.create_subprocess_exec( - 'id', username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode != 0: - # Пользователь не существует, создаем - process = await asyncio.create_subprocess_exec( - 'useradd', '-m', '-s', '/bin/bash', username, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - # Устанавливаем слабый пароль для honeypot - weak_password = username # Очень слабый пароль - process = await asyncio.create_subprocess_exec( - 'chpasswd', - stdin=asyncio.subprocess.PIPE - ) - password_input = f"{username}:{weak_password}\n" - await process.communicate(password_input.encode()) - - logger.info(f"🍯 Honeypot пользователь {username} создан") - else: - logger.error(f"Ошибка создания honeypot пользователя {username}") - - except Exception as e: - logger.error(f"Ошибка создания honeypot пользователя {username}: {e}") \ No newline at end of file diff --git a/.history/src/sessions_20251125195858.py b/.history/src/sessions_20251125195858.py deleted file mode 100644 index e2c6eb0..0000000 --- a/.history/src/sessions_20251125195858.py +++ /dev/null @@ -1,488 +0,0 @@ -""" -Sessions module для PyGuardian -Управление SSH сессиями и процессами пользователей -""" - -import asyncio -import logging -import re -import os -from datetime import datetime -from typing import Dict, List, Optional -import psutil - -logger = logging.getLogger(__name__) - - -class SessionManager: - """Менеджер SSH сессий и пользовательских процессов""" - - def __init__(self): - pass - - async def get_active_sessions(self) -> List[Dict]: - """Получение всех активных SSH сессий""" - try: - sessions = [] - - # Метод 1: через who - who_sessions = await self._get_sessions_via_who() - sessions.extend(who_sessions) - - # Метод 2: через ps (для SSH процессов) - ssh_sessions = await self._get_sessions_via_ps() - sessions.extend(ssh_sessions) - - # Убираем дубликаты и объединяем информацию - unique_sessions = self._merge_session_info(sessions) - - return unique_sessions - - except Exception as e: - logger.error(f"Ошибка получения активных сессий: {e}") - return [] - - async def _get_sessions_via_who(self) -> List[Dict]: - """Получение сессий через команду who""" - try: - sessions = [] - - process = await asyncio.create_subprocess_exec( - 'who', '-u', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - lines = stdout.decode().strip().split('\n') - for line in lines: - if line.strip(): - # Парсим вывод who - # Формат: user tty date time (idle) pid (comment) - match = re.match( - r'(\w+)\s+(\w+)\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+.*?\((\d+)\)', - line - ) - if match: - username, tty, date, time, pid = match.groups() - sessions.append({ - 'username': username, - 'tty': tty, - 'login_date': date, - 'login_time': time, - 'pid': int(pid), - 'type': 'who', - 'status': 'active' - }) - else: - # Альтернативный парсинг для разных форматов who - parts = line.split() - if len(parts) >= 2: - username = parts[0] - tty = parts[1] - - # Ищем PID в скобках - pid_match = re.search(r'\((\d+)\)', line) - pid = int(pid_match.group(1)) if pid_match else None - - sessions.append({ - 'username': username, - 'tty': tty, - 'pid': pid, - 'type': 'who', - 'status': 'active', - 'raw_line': line - }) - - return sessions - - except Exception as e: - logger.error(f"Ошибка получения сессий через who: {e}") - return [] - - async def _get_sessions_via_ps(self) -> List[Dict]: - """Получение SSH сессий через ps""" - try: - sessions = [] - - # Ищем SSH процессы - process = await asyncio.create_subprocess_exec( - 'ps', 'aux', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - lines = stdout.decode().strip().split('\n') - for line in lines[1:]: # Пропускаем заголовок - if 'sshd:' in line and '@pts' in line: - # Парсим SSH сессии - parts = line.split() - if len(parts) >= 11: - username = parts[0] - pid = int(parts[1]) - - # Извлекаем информацию из команды - cmd_parts = ' '.join(parts[10:]) - - # Ищем пользователя и tty в команде sshd - match = re.search(r'sshd:\s+(\w+)@(\w+)', cmd_parts) - if match: - ssh_user, tty = match.groups() - - sessions.append({ - 'username': ssh_user, - 'tty': tty, - 'pid': pid, - 'ppid': int(parts[2]), - 'cpu': parts[2], - 'mem': parts[3], - 'start_time': parts[8], - 'type': 'sshd', - 'status': 'active', - 'command': cmd_parts - }) - - return sessions - - except Exception as e: - logger.error(f"Ошибка получения SSH сессий через ps: {e}") - return [] - - def _merge_session_info(self, sessions: List[Dict]) -> List[Dict]: - """Объединение информации о сессиях и удаление дубликатов""" - try: - merged = {} - - for session in sessions: - key = f"{session['username']}:{session.get('tty', 'unknown')}" - - if key in merged: - # Обновляем существующую запись дополнительной информацией - merged[key].update({k: v for k, v in session.items() if v is not None}) - else: - merged[key] = session.copy() - - # Добавляем дополнительную информацию о процессах - for session in merged.values(): - if session.get('pid'): - try: - # Получаем дополнительную информацию о процессе через psutil - if psutil.pid_exists(session['pid']): - proc = psutil.Process(session['pid']) - session.update({ - 'create_time': datetime.fromtimestamp(proc.create_time()).isoformat(), - 'cpu_percent': proc.cpu_percent(), - 'memory_info': proc.memory_info()._asdict(), - 'connections': len(proc.connections()) - }) - except Exception: - pass # Игнорируем ошибки получения доп. информации - - return list(merged.values()) - - except Exception as e: - logger.error(f"Ошибка объединения информации о сессиях: {e}") - return sessions - - async def get_user_sessions(self, username: str) -> List[Dict]: - """Получение сессий конкретного пользователя""" - try: - all_sessions = await self.get_active_sessions() - return [s for s in all_sessions if s['username'] == username] - - except Exception as e: - logger.error(f"Ошибка получения сессий пользователя {username}: {e}") - return [] - - async def terminate_session(self, pid: int) -> bool: - """Завершение сессии по PID""" - try: - # Сначала пробуем TERM - process = await asyncio.create_subprocess_exec( - 'kill', '-TERM', str(pid), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - # Ждем немного и проверяем - await asyncio.sleep(2) - - if not psutil.pid_exists(pid): - logger.info(f"✅ Сессия PID {pid} завершена через TERM") - return True - else: - # Если не помогло - используем KILL - process = await asyncio.create_subprocess_exec( - 'kill', '-KILL', str(pid), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - logger.info(f"🔪 Сессия PID {pid} принудительно завершена через KILL") - return True - - logger.error(f"❌ Не удалось завершить сессию PID {pid}") - return False - - except Exception as e: - logger.error(f"Ошибка завершения сессии PID {pid}: {e}") - return False - - async def terminate_user_sessions(self, username: str) -> int: - """Завершение всех сессий пользователя""" - try: - user_sessions = await self.get_user_sessions(username) - terminated = 0 - - for session in user_sessions: - pid = session.get('pid') - if pid: - success = await self.terminate_session(pid) - if success: - terminated += 1 - - logger.info(f"Завершено {terminated} из {len(user_sessions)} сессий пользователя {username}") - return terminated - - except Exception as e: - logger.error(f"Ошибка завершения сессий пользователя {username}: {e}") - return 0 - - async def get_session_details(self, pid: int) -> Optional[Dict]: - """Получение детальной информации о сессии""" - try: - if not psutil.pid_exists(pid): - return None - - proc = psutil.Process(pid) - - # Базовая информация о процессе - details = { - 'pid': pid, - 'ppid': proc.ppid(), - 'username': proc.username(), - 'create_time': datetime.fromtimestamp(proc.create_time()).isoformat(), - 'cpu_percent': proc.cpu_percent(), - 'memory_info': proc.memory_info()._asdict(), - 'status': proc.status(), - 'cmdline': proc.cmdline(), - 'cwd': proc.cwd(), - 'exe': proc.exe() - } - - # Сетевые соединения - try: - connections = [] - for conn in proc.connections(): - connections.append({ - 'fd': conn.fd, - 'family': str(conn.family), - 'type': str(conn.type), - 'local_address': f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else None, - 'remote_address': f"{conn.raddr.ip}:{conn.raddr.port}" if conn.raddr else None, - 'status': str(conn.status) - }) - details['connections'] = connections - except Exception: - details['connections'] = [] - - # Открытые файлы - try: - open_files = [] - for file in proc.open_files()[:10]: # Ограничиваем 10 файлами - open_files.append({ - 'path': file.path, - 'fd': file.fd, - 'mode': file.mode - }) - details['open_files'] = open_files - except Exception: - details['open_files'] = [] - - # Переменные окружения (выборочно) - try: - env = proc.environ() - safe_env = {} - safe_keys = ['USER', 'HOME', 'SHELL', 'SSH_CLIENT', 'SSH_CONNECTION', 'TERM'] - for key in safe_keys: - if key in env: - safe_env[key] = env[key] - details['environment'] = safe_env - except Exception: - details['environment'] = {} - - return details - - except Exception as e: - logger.error(f"Ошибка получения деталей сессии PID {pid}: {e}") - return None - - async def monitor_session_activity(self, pid: int, duration: int = 60) -> List[Dict]: - """Мониторинг активности сессии в течение времени""" - try: - if not psutil.pid_exists(pid): - return [] - - activity_log = [] - proc = psutil.Process(pid) - - start_time = datetime.now() - end_time = start_time + timedelta(seconds=duration) - - while datetime.now() < end_time: - try: - # Снимок состояния процесса - snapshot = { - 'timestamp': datetime.now().isoformat(), - 'cpu_percent': proc.cpu_percent(), - 'memory_percent': proc.memory_percent(), - 'num_threads': proc.num_threads(), - 'num_fds': proc.num_fds(), - 'status': proc.status() - } - - # Проверяем новые соединения - try: - connections = len(proc.connections()) - snapshot['connections_count'] = connections - except Exception: - snapshot['connections_count'] = 0 - - activity_log.append(snapshot) - - await asyncio.sleep(5) # Снимок каждые 5 секунд - - except psutil.NoSuchProcess: - # Процесс завершился - activity_log.append({ - 'timestamp': datetime.now().isoformat(), - 'event': 'process_terminated' - }) - break - except Exception as e: - activity_log.append({ - 'timestamp': datetime.now().isoformat(), - 'event': 'monitoring_error', - 'error': str(e) - }) - - return activity_log - - except Exception as e: - logger.error(f"Ошибка мониторинга активности сессии PID {pid}: {e}") - return [] - - async def get_session_statistics(self) -> Dict: - """Получение общей статистики по сессиям""" - try: - sessions = await self.get_active_sessions() - - stats = { - 'total_sessions': len(sessions), - 'users': {}, - 'tty_types': {}, - 'session_ages': [], - 'total_connections': 0 - } - - for session in sessions: - # Статистика по пользователям - user = session['username'] - if user not in stats['users']: - stats['users'][user] = 0 - stats['users'][user] += 1 - - # Статистика по типам TTY - tty = session.get('tty', 'unknown') - tty_type = 'console' if tty.startswith('tty') else 'ssh' - if tty_type not in stats['tty_types']: - stats['tty_types'][tty_type] = 0 - stats['tty_types'][tty_type] += 1 - - # Возраст сессии - if 'create_time' in session: - try: - create_time = datetime.fromisoformat(session['create_time']) - age_seconds = (datetime.now() - create_time).total_seconds() - stats['session_ages'].append(age_seconds) - except Exception: - pass - - # Количество соединений - connections = session.get('connections', 0) - if isinstance(connections, int): - stats['total_connections'] += connections - - # Средний возраст сессий - if stats['session_ages']: - stats['average_session_age'] = sum(stats['session_ages']) / len(stats['session_ages']) - else: - stats['average_session_age'] = 0 - - return stats - - except Exception as e: - logger.error(f"Ошибка получения статистики сессий: {e}") - return {'error': str(e)} - - async def find_suspicious_sessions(self) -> List[Dict]: - """Поиск подозрительных сессий""" - try: - sessions = await self.get_active_sessions() - suspicious = [] - - for session in sessions: - suspicion_score = 0 - reasons = [] - - # Проверка 1: Много открытых соединений - connections = session.get('connections', 0) - if isinstance(connections, int) and connections > 10: - suspicion_score += 2 - reasons.append(f"Много соединений: {connections}") - - # Проверка 2: Высокое потребление CPU - cpu = session.get('cpu_percent', 0) - if isinstance(cpu, (int, float)) and cpu > 50: - suspicion_score += 1 - reasons.append(f"Высокая нагрузка CPU: {cpu}%") - - # Проверка 3: Долго активная сессия - if 'create_time' in session: - try: - create_time = datetime.fromisoformat(session['create_time']) - age_hours = (datetime.now() - create_time).total_seconds() / 3600 - if age_hours > 24: # Больше суток - suspicion_score += 1 - reasons.append(f"Долгая сессия: {age_hours:.1f} часов") - except Exception: - pass - - # Проверка 4: Подозрительные команды в cmdline - cmdline = session.get('cmdline', []) - if isinstance(cmdline, list): - suspicious_commands = ['nc', 'netcat', 'wget', 'curl', 'python', 'perl', 'bash'] - for cmd in cmdline: - if any(susp in cmd.lower() for susp in suspicious_commands): - suspicion_score += 1 - reasons.append(f"Подозрительная команда: {cmd}") - break - - # Если набрали достаточно очков подозрительности - if suspicion_score >= 2: - session['suspicion_score'] = suspicion_score - session['suspicion_reasons'] = reasons - suspicious.append(session) - - return suspicious - - except Exception as e: - logger.error(f"Ошибка поиска подозрительных сессий: {e}") - return [] \ No newline at end of file diff --git a/.history/src/sessions_20251125202055.py b/.history/src/sessions_20251125202055.py deleted file mode 100644 index e2c6eb0..0000000 --- a/.history/src/sessions_20251125202055.py +++ /dev/null @@ -1,488 +0,0 @@ -""" -Sessions module для PyGuardian -Управление SSH сессиями и процессами пользователей -""" - -import asyncio -import logging -import re -import os -from datetime import datetime -from typing import Dict, List, Optional -import psutil - -logger = logging.getLogger(__name__) - - -class SessionManager: - """Менеджер SSH сессий и пользовательских процессов""" - - def __init__(self): - pass - - async def get_active_sessions(self) -> List[Dict]: - """Получение всех активных SSH сессий""" - try: - sessions = [] - - # Метод 1: через who - who_sessions = await self._get_sessions_via_who() - sessions.extend(who_sessions) - - # Метод 2: через ps (для SSH процессов) - ssh_sessions = await self._get_sessions_via_ps() - sessions.extend(ssh_sessions) - - # Убираем дубликаты и объединяем информацию - unique_sessions = self._merge_session_info(sessions) - - return unique_sessions - - except Exception as e: - logger.error(f"Ошибка получения активных сессий: {e}") - return [] - - async def _get_sessions_via_who(self) -> List[Dict]: - """Получение сессий через команду who""" - try: - sessions = [] - - process = await asyncio.create_subprocess_exec( - 'who', '-u', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - lines = stdout.decode().strip().split('\n') - for line in lines: - if line.strip(): - # Парсим вывод who - # Формат: user tty date time (idle) pid (comment) - match = re.match( - r'(\w+)\s+(\w+)\s+(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2})\s+.*?\((\d+)\)', - line - ) - if match: - username, tty, date, time, pid = match.groups() - sessions.append({ - 'username': username, - 'tty': tty, - 'login_date': date, - 'login_time': time, - 'pid': int(pid), - 'type': 'who', - 'status': 'active' - }) - else: - # Альтернативный парсинг для разных форматов who - parts = line.split() - if len(parts) >= 2: - username = parts[0] - tty = parts[1] - - # Ищем PID в скобках - pid_match = re.search(r'\((\d+)\)', line) - pid = int(pid_match.group(1)) if pid_match else None - - sessions.append({ - 'username': username, - 'tty': tty, - 'pid': pid, - 'type': 'who', - 'status': 'active', - 'raw_line': line - }) - - return sessions - - except Exception as e: - logger.error(f"Ошибка получения сессий через who: {e}") - return [] - - async def _get_sessions_via_ps(self) -> List[Dict]: - """Получение SSH сессий через ps""" - try: - sessions = [] - - # Ищем SSH процессы - process = await asyncio.create_subprocess_exec( - 'ps', 'aux', - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - stdout, stderr = await process.communicate() - - if process.returncode == 0: - lines = stdout.decode().strip().split('\n') - for line in lines[1:]: # Пропускаем заголовок - if 'sshd:' in line and '@pts' in line: - # Парсим SSH сессии - parts = line.split() - if len(parts) >= 11: - username = parts[0] - pid = int(parts[1]) - - # Извлекаем информацию из команды - cmd_parts = ' '.join(parts[10:]) - - # Ищем пользователя и tty в команде sshd - match = re.search(r'sshd:\s+(\w+)@(\w+)', cmd_parts) - if match: - ssh_user, tty = match.groups() - - sessions.append({ - 'username': ssh_user, - 'tty': tty, - 'pid': pid, - 'ppid': int(parts[2]), - 'cpu': parts[2], - 'mem': parts[3], - 'start_time': parts[8], - 'type': 'sshd', - 'status': 'active', - 'command': cmd_parts - }) - - return sessions - - except Exception as e: - logger.error(f"Ошибка получения SSH сессий через ps: {e}") - return [] - - def _merge_session_info(self, sessions: List[Dict]) -> List[Dict]: - """Объединение информации о сессиях и удаление дубликатов""" - try: - merged = {} - - for session in sessions: - key = f"{session['username']}:{session.get('tty', 'unknown')}" - - if key in merged: - # Обновляем существующую запись дополнительной информацией - merged[key].update({k: v for k, v in session.items() if v is not None}) - else: - merged[key] = session.copy() - - # Добавляем дополнительную информацию о процессах - for session in merged.values(): - if session.get('pid'): - try: - # Получаем дополнительную информацию о процессе через psutil - if psutil.pid_exists(session['pid']): - proc = psutil.Process(session['pid']) - session.update({ - 'create_time': datetime.fromtimestamp(proc.create_time()).isoformat(), - 'cpu_percent': proc.cpu_percent(), - 'memory_info': proc.memory_info()._asdict(), - 'connections': len(proc.connections()) - }) - except Exception: - pass # Игнорируем ошибки получения доп. информации - - return list(merged.values()) - - except Exception as e: - logger.error(f"Ошибка объединения информации о сессиях: {e}") - return sessions - - async def get_user_sessions(self, username: str) -> List[Dict]: - """Получение сессий конкретного пользователя""" - try: - all_sessions = await self.get_active_sessions() - return [s for s in all_sessions if s['username'] == username] - - except Exception as e: - logger.error(f"Ошибка получения сессий пользователя {username}: {e}") - return [] - - async def terminate_session(self, pid: int) -> bool: - """Завершение сессии по PID""" - try: - # Сначала пробуем TERM - process = await asyncio.create_subprocess_exec( - 'kill', '-TERM', str(pid), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - # Ждем немного и проверяем - await asyncio.sleep(2) - - if not psutil.pid_exists(pid): - logger.info(f"✅ Сессия PID {pid} завершена через TERM") - return True - else: - # Если не помогло - используем KILL - process = await asyncio.create_subprocess_exec( - 'kill', '-KILL', str(pid), - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE - ) - await process.communicate() - - if process.returncode == 0: - logger.info(f"🔪 Сессия PID {pid} принудительно завершена через KILL") - return True - - logger.error(f"❌ Не удалось завершить сессию PID {pid}") - return False - - except Exception as e: - logger.error(f"Ошибка завершения сессии PID {pid}: {e}") - return False - - async def terminate_user_sessions(self, username: str) -> int: - """Завершение всех сессий пользователя""" - try: - user_sessions = await self.get_user_sessions(username) - terminated = 0 - - for session in user_sessions: - pid = session.get('pid') - if pid: - success = await self.terminate_session(pid) - if success: - terminated += 1 - - logger.info(f"Завершено {terminated} из {len(user_sessions)} сессий пользователя {username}") - return terminated - - except Exception as e: - logger.error(f"Ошибка завершения сессий пользователя {username}: {e}") - return 0 - - async def get_session_details(self, pid: int) -> Optional[Dict]: - """Получение детальной информации о сессии""" - try: - if not psutil.pid_exists(pid): - return None - - proc = psutil.Process(pid) - - # Базовая информация о процессе - details = { - 'pid': pid, - 'ppid': proc.ppid(), - 'username': proc.username(), - 'create_time': datetime.fromtimestamp(proc.create_time()).isoformat(), - 'cpu_percent': proc.cpu_percent(), - 'memory_info': proc.memory_info()._asdict(), - 'status': proc.status(), - 'cmdline': proc.cmdline(), - 'cwd': proc.cwd(), - 'exe': proc.exe() - } - - # Сетевые соединения - try: - connections = [] - for conn in proc.connections(): - connections.append({ - 'fd': conn.fd, - 'family': str(conn.family), - 'type': str(conn.type), - 'local_address': f"{conn.laddr.ip}:{conn.laddr.port}" if conn.laddr else None, - 'remote_address': f"{conn.raddr.ip}:{conn.raddr.port}" if conn.raddr else None, - 'status': str(conn.status) - }) - details['connections'] = connections - except Exception: - details['connections'] = [] - - # Открытые файлы - try: - open_files = [] - for file in proc.open_files()[:10]: # Ограничиваем 10 файлами - open_files.append({ - 'path': file.path, - 'fd': file.fd, - 'mode': file.mode - }) - details['open_files'] = open_files - except Exception: - details['open_files'] = [] - - # Переменные окружения (выборочно) - try: - env = proc.environ() - safe_env = {} - safe_keys = ['USER', 'HOME', 'SHELL', 'SSH_CLIENT', 'SSH_CONNECTION', 'TERM'] - for key in safe_keys: - if key in env: - safe_env[key] = env[key] - details['environment'] = safe_env - except Exception: - details['environment'] = {} - - return details - - except Exception as e: - logger.error(f"Ошибка получения деталей сессии PID {pid}: {e}") - return None - - async def monitor_session_activity(self, pid: int, duration: int = 60) -> List[Dict]: - """Мониторинг активности сессии в течение времени""" - try: - if not psutil.pid_exists(pid): - return [] - - activity_log = [] - proc = psutil.Process(pid) - - start_time = datetime.now() - end_time = start_time + timedelta(seconds=duration) - - while datetime.now() < end_time: - try: - # Снимок состояния процесса - snapshot = { - 'timestamp': datetime.now().isoformat(), - 'cpu_percent': proc.cpu_percent(), - 'memory_percent': proc.memory_percent(), - 'num_threads': proc.num_threads(), - 'num_fds': proc.num_fds(), - 'status': proc.status() - } - - # Проверяем новые соединения - try: - connections = len(proc.connections()) - snapshot['connections_count'] = connections - except Exception: - snapshot['connections_count'] = 0 - - activity_log.append(snapshot) - - await asyncio.sleep(5) # Снимок каждые 5 секунд - - except psutil.NoSuchProcess: - # Процесс завершился - activity_log.append({ - 'timestamp': datetime.now().isoformat(), - 'event': 'process_terminated' - }) - break - except Exception as e: - activity_log.append({ - 'timestamp': datetime.now().isoformat(), - 'event': 'monitoring_error', - 'error': str(e) - }) - - return activity_log - - except Exception as e: - logger.error(f"Ошибка мониторинга активности сессии PID {pid}: {e}") - return [] - - async def get_session_statistics(self) -> Dict: - """Получение общей статистики по сессиям""" - try: - sessions = await self.get_active_sessions() - - stats = { - 'total_sessions': len(sessions), - 'users': {}, - 'tty_types': {}, - 'session_ages': [], - 'total_connections': 0 - } - - for session in sessions: - # Статистика по пользователям - user = session['username'] - if user not in stats['users']: - stats['users'][user] = 0 - stats['users'][user] += 1 - - # Статистика по типам TTY - tty = session.get('tty', 'unknown') - tty_type = 'console' if tty.startswith('tty') else 'ssh' - if tty_type not in stats['tty_types']: - stats['tty_types'][tty_type] = 0 - stats['tty_types'][tty_type] += 1 - - # Возраст сессии - if 'create_time' in session: - try: - create_time = datetime.fromisoformat(session['create_time']) - age_seconds = (datetime.now() - create_time).total_seconds() - stats['session_ages'].append(age_seconds) - except Exception: - pass - - # Количество соединений - connections = session.get('connections', 0) - if isinstance(connections, int): - stats['total_connections'] += connections - - # Средний возраст сессий - if stats['session_ages']: - stats['average_session_age'] = sum(stats['session_ages']) / len(stats['session_ages']) - else: - stats['average_session_age'] = 0 - - return stats - - except Exception as e: - logger.error(f"Ошибка получения статистики сессий: {e}") - return {'error': str(e)} - - async def find_suspicious_sessions(self) -> List[Dict]: - """Поиск подозрительных сессий""" - try: - sessions = await self.get_active_sessions() - suspicious = [] - - for session in sessions: - suspicion_score = 0 - reasons = [] - - # Проверка 1: Много открытых соединений - connections = session.get('connections', 0) - if isinstance(connections, int) and connections > 10: - suspicion_score += 2 - reasons.append(f"Много соединений: {connections}") - - # Проверка 2: Высокое потребление CPU - cpu = session.get('cpu_percent', 0) - if isinstance(cpu, (int, float)) and cpu > 50: - suspicion_score += 1 - reasons.append(f"Высокая нагрузка CPU: {cpu}%") - - # Проверка 3: Долго активная сессия - if 'create_time' in session: - try: - create_time = datetime.fromisoformat(session['create_time']) - age_hours = (datetime.now() - create_time).total_seconds() / 3600 - if age_hours > 24: # Больше суток - suspicion_score += 1 - reasons.append(f"Долгая сессия: {age_hours:.1f} часов") - except Exception: - pass - - # Проверка 4: Подозрительные команды в cmdline - cmdline = session.get('cmdline', []) - if isinstance(cmdline, list): - suspicious_commands = ['nc', 'netcat', 'wget', 'curl', 'python', 'perl', 'bash'] - for cmd in cmdline: - if any(susp in cmd.lower() for susp in suspicious_commands): - suspicion_score += 1 - reasons.append(f"Подозрительная команда: {cmd}") - break - - # Если набрали достаточно очков подозрительности - if suspicion_score >= 2: - session['suspicion_score'] = suspicion_score - session['suspicion_reasons'] = reasons - suspicious.append(session) - - return suspicious - - except Exception as e: - logger.error(f"Ошибка поиска подозрительных сессий: {e}") - return [] \ No newline at end of file diff --git a/.history/src/storage_20251125194353.py b/.history/src/storage_20251125194353.py deleted file mode 100644 index b8ad9b7..0000000 --- a/.history/src/storage_20251125194353.py +++ /dev/null @@ -1,413 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: str = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() \ No newline at end of file diff --git a/.history/src/storage_20251125200019.py b/.history/src/storage_20251125200019.py deleted file mode 100644 index 9f255bd..0000000 --- a/.history/src/storage_20251125200019.py +++ /dev/null @@ -1,472 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: str = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() \ No newline at end of file diff --git a/.history/src/storage_20251125200025.py b/.history/src/storage_20251125200025.py deleted file mode 100644 index 3afa86c..0000000 --- a/.history/src/storage_20251125200025.py +++ /dev/null @@ -1,472 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() \ No newline at end of file diff --git a/.history/src/storage_20251125202055.py b/.history/src/storage_20251125202055.py deleted file mode 100644 index 3afa86c..0000000 --- a/.history/src/storage_20251125202055.py +++ /dev/null @@ -1,472 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() \ No newline at end of file diff --git a/.history/src/storage_20251125202250.py b/.history/src/storage_20251125202250.py deleted file mode 100644 index a310f24..0000000 --- a/.history/src/storage_20251125202250.py +++ /dev/null @@ -1,588 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125202304.py b/.history/src/storage_20251125202304.py deleted file mode 100644 index 3429cc0..0000000 --- a/.history/src/storage_20251125202304.py +++ /dev/null @@ -1,608 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125203709.py b/.history/src/storage_20251125203709.py deleted file mode 100644 index 3429cc0..0000000 --- a/.history/src/storage_20251125203709.py +++ /dev/null @@ -1,608 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125205351.py b/.history/src/storage_20251125205351.py deleted file mode 100644 index f831586..0000000 --- a/.history/src/storage_20251125205351.py +++ /dev/null @@ -1,945 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - # Таблица для аутентификационных данных агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth ( - agent_id TEXT PRIMARY KEY, - secret_key_hash TEXT NOT NULL, - salt TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_authenticated DATETIME, - auth_count INTEGER DEFAULT 0, - is_active BOOLEAN DEFAULT 1, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(is_active) - ) - """) - - # Таблица для активных токенов агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_tokens ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - token_hash TEXT NOT NULL, - token_type TEXT NOT NULL, -- 'access' или 'refresh' - expires_at DATETIME NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_used DATETIME, - is_revoked BOOLEAN DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(token_hash), - INDEX(expires_at), - INDEX(is_revoked) - ) - """) - - # Таблица для активных сессий агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_sessions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - session_id TEXT UNIQUE NOT NULL, - ip_address TEXT NOT NULL, - user_agent TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_activity DATETIME DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME NOT NULL, - is_active BOOLEAN DEFAULT 1, - requests_count INTEGER DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(session_id), - INDEX(ip_address), - INDEX(expires_at), - INDEX(is_active) - ) - """) - - # Таблица для логов аутентификации агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth_logs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - ip_address TEXT NOT NULL, - action TEXT NOT NULL, -- 'login', 'logout', 'token_refresh', 'access_denied' - success BOOLEAN NOT NULL, - error_message TEXT, - user_agent TEXT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(agent_id), - INDEX(ip_address), - INDEX(action), - INDEX(timestamp) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def create_agent_auth(self, agent_id: str, secret_key_hash: str, salt: str) -> bool: - """Создать аутентификационные данные для агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agent_auth - (agent_id, secret_key_hash, salt, created_at) - VALUES (?, ?, ?, ?) - """, (agent_id, secret_key_hash, salt, datetime.now())) - await db.commit() - logger.info(f"Created auth data for agent {agent_id}") - return True - except Exception as e: - logger.error(f"Failed to create auth data for agent {agent_id}: {e}") - return False - - async def get_agent_auth(self, agent_id: str) -> Optional[Dict[str, str]]: - """Получить аутентификационные данные агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT secret_key_hash, salt, last_authenticated, auth_count, is_active - FROM agent_auth WHERE agent_id = ? AND is_active = 1 - """, (agent_id,)) - result = await cursor.fetchone() - - if result: - return { - 'secret_key_hash': result[0], - 'salt': result[1], - 'last_authenticated': result[2], - 'auth_count': result[3], - 'is_active': bool(result[4]) - } - return None - except Exception as e: - logger.error(f"Failed to get auth data for agent {agent_id}: {e}") - return None - - async def update_agent_last_auth(self, agent_id: str) -> bool: - """Обновить время последней аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_auth - SET last_authenticated = ?, auth_count = auth_count + 1 - WHERE agent_id = ? - """, (datetime.now(), agent_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update last auth for agent {agent_id}: {e}") - return False - - async def store_agent_token(self, agent_id: str, token_hash: str, - token_type: str, expires_at: datetime) -> bool: - """Сохранить токен агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_tokens - (agent_id, token_hash, token_type, expires_at) - VALUES (?, ?, ?, ?) - """, (agent_id, token_hash, token_type, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to store token for agent {agent_id}: {e}") - return False - - async def verify_agent_token(self, agent_id: str, token_hash: str) -> bool: - """Проверить действительность токена агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT id FROM agent_tokens - WHERE agent_id = ? AND token_hash = ? - AND expires_at > ? AND is_revoked = 0 - """, (agent_id, token_hash, datetime.now())) - result = await cursor.fetchone() - - if result: - # Обновить время последнего использования - await db.execute(""" - UPDATE agent_tokens SET last_used = ? WHERE id = ? - """, (datetime.now(), result[0])) - await db.commit() - return True - return False - except Exception as e: - logger.error(f"Failed to verify token for agent {agent_id}: {e}") - return False - - async def revoke_agent_tokens(self, agent_id: str, token_type: Optional[str] = None) -> bool: - """Отозвать токены агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - if token_type: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? AND token_type = ? - """, (agent_id, token_type)) - else: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? - """, (agent_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to revoke tokens for agent {agent_id}: {e}") - return False - - async def create_agent_session(self, agent_id: str, session_id: str, - ip_address: str, expires_at: datetime, - user_agent: Optional[str] = None) -> bool: - """Создать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_sessions - (agent_id, session_id, ip_address, user_agent, expires_at) - VALUES (?, ?, ?, ?, ?) - """, (agent_id, session_id, ip_address, user_agent, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to create session for agent {agent_id}: {e}") - return False - - async def update_agent_session_activity(self, session_id: str) -> bool: - """Обновить активность сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions - SET last_activity = ?, requests_count = requests_count + 1 - WHERE session_id = ? AND is_active = 1 - """, (datetime.now(), session_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update session activity {session_id}: {e}") - return False - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT session_id, ip_address, user_agent, created_at, - last_activity, requests_count - FROM agent_sessions - WHERE agent_id = ? AND is_active = 1 AND expires_at > ? - """, (agent_id, datetime.now())) - results = await cursor.fetchall() - - sessions = [] - for row in results: - sessions.append({ - 'session_id': row[0], - 'ip_address': row[1], - 'user_agent': row[2], - 'created_at': row[3], - 'last_activity': row[4], - 'requests_count': row[5] - }) - return sessions - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def deactivate_agent_session(self, session_id: str) -> bool: - """Деактивировать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions SET is_active = 0 WHERE session_id = ? - """, (session_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to deactivate session {session_id}: {e}") - return False - - async def log_agent_auth_event(self, agent_id: str, ip_address: str, - action: str, success: bool, - error_message: Optional[str] = None, - user_agent: Optional[str] = None) -> bool: - """Записать событие аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_auth_logs - (agent_id, ip_address, action, success, error_message, user_agent) - VALUES (?, ?, ?, ?, ?, ?) - """, (agent_id, ip_address, action, success, error_message, user_agent)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to log auth event for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 100) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT ip_address, action, success, error_message, - user_agent, timestamp - FROM agent_auth_logs - WHERE agent_id = ? - ORDER BY timestamp DESC LIMIT ? - """, (agent_id, limit)) - results = await cursor.fetchall() - - logs = [] - for row in results: - logs.append({ - 'ip_address': row[0], - 'action': row[1], - 'success': bool(row[2]), - 'error_message': row[3], - 'user_agent': row[4], - 'timestamp': row[5] - }) - return logs - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - DELETE FROM agent_tokens WHERE expires_at < ? - """, (datetime.now(),)) - await db.commit() - deleted_count = cursor.rowcount - logger.info(f"Cleaned up {deleted_count} expired tokens") - return deleted_count - except Exception as e: - logger.error(f"Failed to cleanup expired tokens: {e}") - return 0 - - async def cleanup_expired_sessions(self) -> int: - """Очистка истекших сессий""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - UPDATE agent_sessions SET is_active = 0 - WHERE expires_at < ? AND is_active = 1 - """, (datetime.now(),)) - await db.commit() - cleaned_count = cursor.rowcount - logger.info(f"Cleaned up {cleaned_count} expired sessions") - return cleaned_count - except Exception as e: - logger.error(f"Failed to cleanup expired sessions: {e}") - return 0 - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125205402.py b/.history/src/storage_20251125205402.py deleted file mode 100644 index c7e2749..0000000 --- a/.history/src/storage_20251125205402.py +++ /dev/null @@ -1,945 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - # Таблица для аутентификационных данных агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth ( - agent_id TEXT PRIMARY KEY, - secret_key_hash TEXT NOT NULL, - salt TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_authenticated DATETIME, - auth_count INTEGER DEFAULT 0, - is_active BOOLEAN DEFAULT 1, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(is_active) - ) - """) - - # Таблица для активных токенов агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_tokens ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - token_hash TEXT NOT NULL, - token_type TEXT NOT NULL, -- 'access' или 'refresh' - expires_at DATETIME NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_used DATETIME, - is_revoked BOOLEAN DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(token_hash), - INDEX(expires_at), - INDEX(is_revoked) - ) - """) - - # Таблица для активных сессий агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_sessions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - session_id TEXT UNIQUE NOT NULL, - ip_address TEXT NOT NULL, - user_agent TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_activity DATETIME DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME NOT NULL, - is_active BOOLEAN DEFAULT 1, - requests_count INTEGER DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(session_id), - INDEX(ip_address), - INDEX(expires_at), - INDEX(is_active) - ) - """) - - # Таблица для логов аутентификации агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth_logs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - ip_address TEXT NOT NULL, - action TEXT NOT NULL, -- 'login', 'logout', 'token_refresh', 'access_denied' - success BOOLEAN NOT NULL, - error_message TEXT, - user_agent TEXT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(agent_id), - INDEX(ip_address), - INDEX(action), - INDEX(timestamp) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def create_agent_auth(self, agent_id: str, secret_key_hash: str, salt: str) -> bool: - """Создать аутентификационные данные для агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agent_auth - (agent_id, secret_key_hash, salt, created_at) - VALUES (?, ?, ?, ?) - """, (agent_id, secret_key_hash, salt, datetime.now())) - await db.commit() - logger.info(f"Created auth data for agent {agent_id}") - return True - except Exception as e: - logger.error(f"Failed to create auth data for agent {agent_id}: {e}") - return False - - async def get_agent_auth(self, agent_id: str) -> Optional[Dict[str, any]]: - """Получить аутентификационные данные агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT secret_key_hash, salt, last_authenticated, auth_count, is_active - FROM agent_auth WHERE agent_id = ? AND is_active = 1 - """, (agent_id,)) - result = await cursor.fetchone() - - if result: - return { - 'secret_key_hash': result[0], - 'salt': result[1], - 'last_authenticated': result[2], - 'auth_count': result[3], - 'is_active': bool(result[4]) - } - return None - except Exception as e: - logger.error(f"Failed to get auth data for agent {agent_id}: {e}") - return None - - async def update_agent_last_auth(self, agent_id: str) -> bool: - """Обновить время последней аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_auth - SET last_authenticated = ?, auth_count = auth_count + 1 - WHERE agent_id = ? - """, (datetime.now(), agent_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update last auth for agent {agent_id}: {e}") - return False - - async def store_agent_token(self, agent_id: str, token_hash: str, - token_type: str, expires_at: datetime) -> bool: - """Сохранить токен агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_tokens - (agent_id, token_hash, token_type, expires_at) - VALUES (?, ?, ?, ?) - """, (agent_id, token_hash, token_type, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to store token for agent {agent_id}: {e}") - return False - - async def verify_agent_token(self, agent_id: str, token_hash: str) -> bool: - """Проверить действительность токена агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT id FROM agent_tokens - WHERE agent_id = ? AND token_hash = ? - AND expires_at > ? AND is_revoked = 0 - """, (agent_id, token_hash, datetime.now())) - result = await cursor.fetchone() - - if result: - # Обновить время последнего использования - await db.execute(""" - UPDATE agent_tokens SET last_used = ? WHERE id = ? - """, (datetime.now(), result[0])) - await db.commit() - return True - return False - except Exception as e: - logger.error(f"Failed to verify token for agent {agent_id}: {e}") - return False - - async def revoke_agent_tokens(self, agent_id: str, token_type: Optional[str] = None) -> bool: - """Отозвать токены агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - if token_type: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? AND token_type = ? - """, (agent_id, token_type)) - else: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? - """, (agent_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to revoke tokens for agent {agent_id}: {e}") - return False - - async def create_agent_session(self, agent_id: str, session_id: str, - ip_address: str, expires_at: datetime, - user_agent: Optional[str] = None) -> bool: - """Создать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_sessions - (agent_id, session_id, ip_address, user_agent, expires_at) - VALUES (?, ?, ?, ?, ?) - """, (agent_id, session_id, ip_address, user_agent, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to create session for agent {agent_id}: {e}") - return False - - async def update_agent_session_activity(self, session_id: str) -> bool: - """Обновить активность сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions - SET last_activity = ?, requests_count = requests_count + 1 - WHERE session_id = ? AND is_active = 1 - """, (datetime.now(), session_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update session activity {session_id}: {e}") - return False - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT session_id, ip_address, user_agent, created_at, - last_activity, requests_count - FROM agent_sessions - WHERE agent_id = ? AND is_active = 1 AND expires_at > ? - """, (agent_id, datetime.now())) - results = await cursor.fetchall() - - sessions = [] - for row in results: - sessions.append({ - 'session_id': row[0], - 'ip_address': row[1], - 'user_agent': row[2], - 'created_at': row[3], - 'last_activity': row[4], - 'requests_count': row[5] - }) - return sessions - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def deactivate_agent_session(self, session_id: str) -> bool: - """Деактивировать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions SET is_active = 0 WHERE session_id = ? - """, (session_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to deactivate session {session_id}: {e}") - return False - - async def log_agent_auth_event(self, agent_id: str, ip_address: str, - action: str, success: bool, - error_message: Optional[str] = None, - user_agent: Optional[str] = None) -> bool: - """Записать событие аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_auth_logs - (agent_id, ip_address, action, success, error_message, user_agent) - VALUES (?, ?, ?, ?, ?, ?) - """, (agent_id, ip_address, action, success, error_message, user_agent)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to log auth event for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 100) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT ip_address, action, success, error_message, - user_agent, timestamp - FROM agent_auth_logs - WHERE agent_id = ? - ORDER BY timestamp DESC LIMIT ? - """, (agent_id, limit)) - results = await cursor.fetchall() - - logs = [] - for row in results: - logs.append({ - 'ip_address': row[0], - 'action': row[1], - 'success': bool(row[2]), - 'error_message': row[3], - 'user_agent': row[4], - 'timestamp': row[5] - }) - return logs - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - DELETE FROM agent_tokens WHERE expires_at < ? - """, (datetime.now(),)) - await db.commit() - deleted_count = cursor.rowcount - logger.info(f"Cleaned up {deleted_count} expired tokens") - return deleted_count - except Exception as e: - logger.error(f"Failed to cleanup expired tokens: {e}") - return 0 - - async def cleanup_expired_sessions(self) -> int: - """Очистка истекших сессий""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - UPDATE agent_sessions SET is_active = 0 - WHERE expires_at < ? AND is_active = 1 - """, (datetime.now(),)) - await db.commit() - cleaned_count = cursor.rowcount - logger.info(f"Cleaned up {cleaned_count} expired sessions") - return cleaned_count - except Exception as e: - logger.error(f"Failed to cleanup expired sessions: {e}") - return 0 - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125205407.py b/.history/src/storage_20251125205407.py deleted file mode 100644 index cb42de3..0000000 --- a/.history/src/storage_20251125205407.py +++ /dev/null @@ -1,945 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - # Таблица для аутентификационных данных агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth ( - agent_id TEXT PRIMARY KEY, - secret_key_hash TEXT NOT NULL, - salt TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_authenticated DATETIME, - auth_count INTEGER DEFAULT 0, - is_active BOOLEAN DEFAULT 1, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(is_active) - ) - """) - - # Таблица для активных токенов агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_tokens ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - token_hash TEXT NOT NULL, - token_type TEXT NOT NULL, -- 'access' или 'refresh' - expires_at DATETIME NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_used DATETIME, - is_revoked BOOLEAN DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(token_hash), - INDEX(expires_at), - INDEX(is_revoked) - ) - """) - - # Таблица для активных сессий агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_sessions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - session_id TEXT UNIQUE NOT NULL, - ip_address TEXT NOT NULL, - user_agent TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_activity DATETIME DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME NOT NULL, - is_active BOOLEAN DEFAULT 1, - requests_count INTEGER DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(session_id), - INDEX(ip_address), - INDEX(expires_at), - INDEX(is_active) - ) - """) - - # Таблица для логов аутентификации агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth_logs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - ip_address TEXT NOT NULL, - action TEXT NOT NULL, -- 'login', 'logout', 'token_refresh', 'access_denied' - success BOOLEAN NOT NULL, - error_message TEXT, - user_agent TEXT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(agent_id), - INDEX(ip_address), - INDEX(action), - INDEX(timestamp) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def create_agent_auth(self, agent_id: str, secret_key_hash: str, salt: str) -> bool: - """Создать аутентификационные данные для агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agent_auth - (agent_id, secret_key_hash, salt, created_at) - VALUES (?, ?, ?, ?) - """, (agent_id, secret_key_hash, salt, datetime.now())) - await db.commit() - logger.info(f"Created auth data for agent {agent_id}") - return True - except Exception as e: - logger.error(f"Failed to create auth data for agent {agent_id}: {e}") - return False - - async def get_agent_auth(self, agent_id: str) -> Optional[Dict[str, Any]]: - """Получить аутентификационные данные агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT secret_key_hash, salt, last_authenticated, auth_count, is_active - FROM agent_auth WHERE agent_id = ? AND is_active = 1 - """, (agent_id,)) - result = await cursor.fetchone() - - if result: - return { - 'secret_key_hash': result[0], - 'salt': result[1], - 'last_authenticated': result[2], - 'auth_count': result[3], - 'is_active': bool(result[4]) - } - return None - except Exception as e: - logger.error(f"Failed to get auth data for agent {agent_id}: {e}") - return None - - async def update_agent_last_auth(self, agent_id: str) -> bool: - """Обновить время последней аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_auth - SET last_authenticated = ?, auth_count = auth_count + 1 - WHERE agent_id = ? - """, (datetime.now(), agent_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update last auth for agent {agent_id}: {e}") - return False - - async def store_agent_token(self, agent_id: str, token_hash: str, - token_type: str, expires_at: datetime) -> bool: - """Сохранить токен агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_tokens - (agent_id, token_hash, token_type, expires_at) - VALUES (?, ?, ?, ?) - """, (agent_id, token_hash, token_type, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to store token for agent {agent_id}: {e}") - return False - - async def verify_agent_token(self, agent_id: str, token_hash: str) -> bool: - """Проверить действительность токена агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT id FROM agent_tokens - WHERE agent_id = ? AND token_hash = ? - AND expires_at > ? AND is_revoked = 0 - """, (agent_id, token_hash, datetime.now())) - result = await cursor.fetchone() - - if result: - # Обновить время последнего использования - await db.execute(""" - UPDATE agent_tokens SET last_used = ? WHERE id = ? - """, (datetime.now(), result[0])) - await db.commit() - return True - return False - except Exception as e: - logger.error(f"Failed to verify token for agent {agent_id}: {e}") - return False - - async def revoke_agent_tokens(self, agent_id: str, token_type: Optional[str] = None) -> bool: - """Отозвать токены агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - if token_type: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? AND token_type = ? - """, (agent_id, token_type)) - else: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? - """, (agent_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to revoke tokens for agent {agent_id}: {e}") - return False - - async def create_agent_session(self, agent_id: str, session_id: str, - ip_address: str, expires_at: datetime, - user_agent: Optional[str] = None) -> bool: - """Создать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_sessions - (agent_id, session_id, ip_address, user_agent, expires_at) - VALUES (?, ?, ?, ?, ?) - """, (agent_id, session_id, ip_address, user_agent, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to create session for agent {agent_id}: {e}") - return False - - async def update_agent_session_activity(self, session_id: str) -> bool: - """Обновить активность сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions - SET last_activity = ?, requests_count = requests_count + 1 - WHERE session_id = ? AND is_active = 1 - """, (datetime.now(), session_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update session activity {session_id}: {e}") - return False - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT session_id, ip_address, user_agent, created_at, - last_activity, requests_count - FROM agent_sessions - WHERE agent_id = ? AND is_active = 1 AND expires_at > ? - """, (agent_id, datetime.now())) - results = await cursor.fetchall() - - sessions = [] - for row in results: - sessions.append({ - 'session_id': row[0], - 'ip_address': row[1], - 'user_agent': row[2], - 'created_at': row[3], - 'last_activity': row[4], - 'requests_count': row[5] - }) - return sessions - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def deactivate_agent_session(self, session_id: str) -> bool: - """Деактивировать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions SET is_active = 0 WHERE session_id = ? - """, (session_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to deactivate session {session_id}: {e}") - return False - - async def log_agent_auth_event(self, agent_id: str, ip_address: str, - action: str, success: bool, - error_message: Optional[str] = None, - user_agent: Optional[str] = None) -> bool: - """Записать событие аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_auth_logs - (agent_id, ip_address, action, success, error_message, user_agent) - VALUES (?, ?, ?, ?, ?, ?) - """, (agent_id, ip_address, action, success, error_message, user_agent)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to log auth event for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 100) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT ip_address, action, success, error_message, - user_agent, timestamp - FROM agent_auth_logs - WHERE agent_id = ? - ORDER BY timestamp DESC LIMIT ? - """, (agent_id, limit)) - results = await cursor.fetchall() - - logs = [] - for row in results: - logs.append({ - 'ip_address': row[0], - 'action': row[1], - 'success': bool(row[2]), - 'error_message': row[3], - 'user_agent': row[4], - 'timestamp': row[5] - }) - return logs - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - DELETE FROM agent_tokens WHERE expires_at < ? - """, (datetime.now(),)) - await db.commit() - deleted_count = cursor.rowcount - logger.info(f"Cleaned up {deleted_count} expired tokens") - return deleted_count - except Exception as e: - logger.error(f"Failed to cleanup expired tokens: {e}") - return 0 - - async def cleanup_expired_sessions(self) -> int: - """Очистка истекших сессий""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - UPDATE agent_sessions SET is_active = 0 - WHERE expires_at < ? AND is_active = 1 - """, (datetime.now(),)) - await db.commit() - cleaned_count = cursor.rowcount - logger.info(f"Cleaned up {cleaned_count} expired sessions") - return cleaned_count - except Exception as e: - logger.error(f"Failed to cleanup expired sessions: {e}") - return 0 - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125205413.py b/.history/src/storage_20251125205413.py deleted file mode 100644 index 67e9887..0000000 --- a/.history/src/storage_20251125205413.py +++ /dev/null @@ -1,945 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple, Any -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - # Таблица для аутентификационных данных агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth ( - agent_id TEXT PRIMARY KEY, - secret_key_hash TEXT NOT NULL, - salt TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_authenticated DATETIME, - auth_count INTEGER DEFAULT 0, - is_active BOOLEAN DEFAULT 1, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(is_active) - ) - """) - - # Таблица для активных токенов агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_tokens ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - token_hash TEXT NOT NULL, - token_type TEXT NOT NULL, -- 'access' или 'refresh' - expires_at DATETIME NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_used DATETIME, - is_revoked BOOLEAN DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(token_hash), - INDEX(expires_at), - INDEX(is_revoked) - ) - """) - - # Таблица для активных сессий агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_sessions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - session_id TEXT UNIQUE NOT NULL, - ip_address TEXT NOT NULL, - user_agent TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_activity DATETIME DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME NOT NULL, - is_active BOOLEAN DEFAULT 1, - requests_count INTEGER DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(session_id), - INDEX(ip_address), - INDEX(expires_at), - INDEX(is_active) - ) - """) - - # Таблица для логов аутентификации агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth_logs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - ip_address TEXT NOT NULL, - action TEXT NOT NULL, -- 'login', 'logout', 'token_refresh', 'access_denied' - success BOOLEAN NOT NULL, - error_message TEXT, - user_agent TEXT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(agent_id), - INDEX(ip_address), - INDEX(action), - INDEX(timestamp) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def create_agent_auth(self, agent_id: str, secret_key_hash: str, salt: str) -> bool: - """Создать аутентификационные данные для агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agent_auth - (agent_id, secret_key_hash, salt, created_at) - VALUES (?, ?, ?, ?) - """, (agent_id, secret_key_hash, salt, datetime.now())) - await db.commit() - logger.info(f"Created auth data for agent {agent_id}") - return True - except Exception as e: - logger.error(f"Failed to create auth data for agent {agent_id}: {e}") - return False - - async def get_agent_auth(self, agent_id: str) -> Optional[Dict[str, Any]]: - """Получить аутентификационные данные агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT secret_key_hash, salt, last_authenticated, auth_count, is_active - FROM agent_auth WHERE agent_id = ? AND is_active = 1 - """, (agent_id,)) - result = await cursor.fetchone() - - if result: - return { - 'secret_key_hash': result[0], - 'salt': result[1], - 'last_authenticated': result[2], - 'auth_count': result[3], - 'is_active': bool(result[4]) - } - return None - except Exception as e: - logger.error(f"Failed to get auth data for agent {agent_id}: {e}") - return None - - async def update_agent_last_auth(self, agent_id: str) -> bool: - """Обновить время последней аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_auth - SET last_authenticated = ?, auth_count = auth_count + 1 - WHERE agent_id = ? - """, (datetime.now(), agent_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update last auth for agent {agent_id}: {e}") - return False - - async def store_agent_token(self, agent_id: str, token_hash: str, - token_type: str, expires_at: datetime) -> bool: - """Сохранить токен агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_tokens - (agent_id, token_hash, token_type, expires_at) - VALUES (?, ?, ?, ?) - """, (agent_id, token_hash, token_type, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to store token for agent {agent_id}: {e}") - return False - - async def verify_agent_token(self, agent_id: str, token_hash: str) -> bool: - """Проверить действительность токена агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT id FROM agent_tokens - WHERE agent_id = ? AND token_hash = ? - AND expires_at > ? AND is_revoked = 0 - """, (agent_id, token_hash, datetime.now())) - result = await cursor.fetchone() - - if result: - # Обновить время последнего использования - await db.execute(""" - UPDATE agent_tokens SET last_used = ? WHERE id = ? - """, (datetime.now(), result[0])) - await db.commit() - return True - return False - except Exception as e: - logger.error(f"Failed to verify token for agent {agent_id}: {e}") - return False - - async def revoke_agent_tokens(self, agent_id: str, token_type: Optional[str] = None) -> bool: - """Отозвать токены агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - if token_type: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? AND token_type = ? - """, (agent_id, token_type)) - else: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? - """, (agent_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to revoke tokens for agent {agent_id}: {e}") - return False - - async def create_agent_session(self, agent_id: str, session_id: str, - ip_address: str, expires_at: datetime, - user_agent: Optional[str] = None) -> bool: - """Создать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_sessions - (agent_id, session_id, ip_address, user_agent, expires_at) - VALUES (?, ?, ?, ?, ?) - """, (agent_id, session_id, ip_address, user_agent, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to create session for agent {agent_id}: {e}") - return False - - async def update_agent_session_activity(self, session_id: str) -> bool: - """Обновить активность сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions - SET last_activity = ?, requests_count = requests_count + 1 - WHERE session_id = ? AND is_active = 1 - """, (datetime.now(), session_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update session activity {session_id}: {e}") - return False - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT session_id, ip_address, user_agent, created_at, - last_activity, requests_count - FROM agent_sessions - WHERE agent_id = ? AND is_active = 1 AND expires_at > ? - """, (agent_id, datetime.now())) - results = await cursor.fetchall() - - sessions = [] - for row in results: - sessions.append({ - 'session_id': row[0], - 'ip_address': row[1], - 'user_agent': row[2], - 'created_at': row[3], - 'last_activity': row[4], - 'requests_count': row[5] - }) - return sessions - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def deactivate_agent_session(self, session_id: str) -> bool: - """Деактивировать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions SET is_active = 0 WHERE session_id = ? - """, (session_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to deactivate session {session_id}: {e}") - return False - - async def log_agent_auth_event(self, agent_id: str, ip_address: str, - action: str, success: bool, - error_message: Optional[str] = None, - user_agent: Optional[str] = None) -> bool: - """Записать событие аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_auth_logs - (agent_id, ip_address, action, success, error_message, user_agent) - VALUES (?, ?, ?, ?, ?, ?) - """, (agent_id, ip_address, action, success, error_message, user_agent)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to log auth event for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 100) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT ip_address, action, success, error_message, - user_agent, timestamp - FROM agent_auth_logs - WHERE agent_id = ? - ORDER BY timestamp DESC LIMIT ? - """, (agent_id, limit)) - results = await cursor.fetchall() - - logs = [] - for row in results: - logs.append({ - 'ip_address': row[0], - 'action': row[1], - 'success': bool(row[2]), - 'error_message': row[3], - 'user_agent': row[4], - 'timestamp': row[5] - }) - return logs - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - DELETE FROM agent_tokens WHERE expires_at < ? - """, (datetime.now(),)) - await db.commit() - deleted_count = cursor.rowcount - logger.info(f"Cleaned up {deleted_count} expired tokens") - return deleted_count - except Exception as e: - logger.error(f"Failed to cleanup expired tokens: {e}") - return 0 - - async def cleanup_expired_sessions(self) -> int: - """Очистка истекших сессий""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - UPDATE agent_sessions SET is_active = 0 - WHERE expires_at < ? AND is_active = 1 - """, (datetime.now(),)) - await db.commit() - cleaned_count = cursor.rowcount - logger.info(f"Cleaned up {cleaned_count} expired sessions") - return cleaned_count - except Exception as e: - logger.error(f"Failed to cleanup expired sessions: {e}") - return 0 - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/src/storage_20251125210433.py b/.history/src/storage_20251125210433.py deleted file mode 100644 index 67e9887..0000000 --- a/.history/src/storage_20251125210433.py +++ /dev/null @@ -1,945 +0,0 @@ -""" -Storage module для PyGuardian -Управление SQLite базой данных для хранения IP-адресов, попыток атак и банов -""" - -import asyncio -import sqlite3 -import aiosqlite -import ipaddress -import json -from datetime import datetime, timedelta -from typing import List, Dict, Optional, Tuple, Any -import logging - -logger = logging.getLogger(__name__) - - -class Storage: - """Асинхронный класс для работы с SQLite базой данных""" - - def __init__(self, db_path: str): - self.db_path = db_path - self._connection = None - - async def init_database(self) -> None: - """Инициализация базы данных и создание таблиц""" - async with aiosqlite.connect(self.db_path) as db: - # Таблица для хранения попыток атак - await db.execute(""" - CREATE TABLE IF NOT EXISTS attack_attempts ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT, - attack_type TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - log_line TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(timestamp) - ) - """) - - # Таблица для хранения забаненных IP - await db.execute(""" - CREATE TABLE IF NOT EXISTS banned_ips ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT UNIQUE NOT NULL, - ban_reason TEXT NOT NULL, - banned_at DATETIME DEFAULT CURRENT_TIMESTAMP, - unban_at DATETIME, - is_active BOOLEAN DEFAULT 1, - manual_ban BOOLEAN DEFAULT 0, - attempts_count INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(unban_at), - INDEX(is_active) - ) - """) - - # Таблица для успешных входов - await db.execute(""" - CREATE TABLE IF NOT EXISTS successful_logins ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - session_info TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(timestamp) - ) - """) - - # Таблица для статистики - await db.execute(""" - CREATE TABLE IF NOT EXISTS daily_stats ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - date DATE UNIQUE NOT NULL, - total_attempts INTEGER DEFAULT 0, - unique_ips INTEGER DEFAULT 0, - banned_count INTEGER DEFAULT 0, - successful_logins INTEGER DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(date) - ) - """) - - # Таблица для компрометаций - await db.execute(""" - CREATE TABLE IF NOT EXISTS compromises ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - ip_address TEXT NOT NULL, - username TEXT NOT NULL, - detection_time DATETIME NOT NULL, - session_active BOOLEAN DEFAULT 1, - new_password TEXT, - session_info TEXT, - resolved BOOLEAN DEFAULT 0, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(ip_address), - INDEX(username), - INDEX(detection_time) - ) - """) - - # Таблица для агентов кластера - await db.execute(""" - CREATE TABLE IF NOT EXISTS agents ( - agent_id TEXT PRIMARY KEY, - hostname TEXT NOT NULL, - ip_address TEXT NOT NULL, - ssh_port INTEGER DEFAULT 22, - ssh_user TEXT DEFAULT 'root', - status TEXT DEFAULT 'added', - added_time DATETIME NOT NULL, - last_check DATETIME, - version TEXT, - config TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(hostname), - INDEX(ip_address), - INDEX(status) - ) - """) - - # Таблица для аутентификационных данных агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth ( - agent_id TEXT PRIMARY KEY, - secret_key_hash TEXT NOT NULL, - salt TEXT NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_authenticated DATETIME, - auth_count INTEGER DEFAULT 0, - is_active BOOLEAN DEFAULT 1, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(is_active) - ) - """) - - # Таблица для активных токенов агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_tokens ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - token_hash TEXT NOT NULL, - token_type TEXT NOT NULL, -- 'access' или 'refresh' - expires_at DATETIME NOT NULL, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_used DATETIME, - is_revoked BOOLEAN DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(token_hash), - INDEX(expires_at), - INDEX(is_revoked) - ) - """) - - # Таблица для активных сессий агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_sessions ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT NOT NULL, - session_id TEXT UNIQUE NOT NULL, - ip_address TEXT NOT NULL, - user_agent TEXT, - created_at DATETIME DEFAULT CURRENT_TIMESTAMP, - last_activity DATETIME DEFAULT CURRENT_TIMESTAMP, - expires_at DATETIME NOT NULL, - is_active BOOLEAN DEFAULT 1, - requests_count INTEGER DEFAULT 0, - FOREIGN KEY(agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE, - INDEX(agent_id), - INDEX(session_id), - INDEX(ip_address), - INDEX(expires_at), - INDEX(is_active) - ) - """) - - # Таблица для логов аутентификации агентов - await db.execute(""" - CREATE TABLE IF NOT EXISTS agent_auth_logs ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - agent_id TEXT, - ip_address TEXT NOT NULL, - action TEXT NOT NULL, -- 'login', 'logout', 'token_refresh', 'access_denied' - success BOOLEAN NOT NULL, - error_message TEXT, - user_agent TEXT, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX(agent_id), - INDEX(ip_address), - INDEX(action), - INDEX(timestamp) - ) - """) - - await db.commit() - logger.info("База данных инициализирована успешно") - - async def create_agent_auth(self, agent_id: str, secret_key_hash: str, salt: str) -> bool: - """Создать аутентификационные данные для агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agent_auth - (agent_id, secret_key_hash, salt, created_at) - VALUES (?, ?, ?, ?) - """, (agent_id, secret_key_hash, salt, datetime.now())) - await db.commit() - logger.info(f"Created auth data for agent {agent_id}") - return True - except Exception as e: - logger.error(f"Failed to create auth data for agent {agent_id}: {e}") - return False - - async def get_agent_auth(self, agent_id: str) -> Optional[Dict[str, Any]]: - """Получить аутентификационные данные агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT secret_key_hash, salt, last_authenticated, auth_count, is_active - FROM agent_auth WHERE agent_id = ? AND is_active = 1 - """, (agent_id,)) - result = await cursor.fetchone() - - if result: - return { - 'secret_key_hash': result[0], - 'salt': result[1], - 'last_authenticated': result[2], - 'auth_count': result[3], - 'is_active': bool(result[4]) - } - return None - except Exception as e: - logger.error(f"Failed to get auth data for agent {agent_id}: {e}") - return None - - async def update_agent_last_auth(self, agent_id: str) -> bool: - """Обновить время последней аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_auth - SET last_authenticated = ?, auth_count = auth_count + 1 - WHERE agent_id = ? - """, (datetime.now(), agent_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update last auth for agent {agent_id}: {e}") - return False - - async def store_agent_token(self, agent_id: str, token_hash: str, - token_type: str, expires_at: datetime) -> bool: - """Сохранить токен агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_tokens - (agent_id, token_hash, token_type, expires_at) - VALUES (?, ?, ?, ?) - """, (agent_id, token_hash, token_type, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to store token for agent {agent_id}: {e}") - return False - - async def verify_agent_token(self, agent_id: str, token_hash: str) -> bool: - """Проверить действительность токена агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT id FROM agent_tokens - WHERE agent_id = ? AND token_hash = ? - AND expires_at > ? AND is_revoked = 0 - """, (agent_id, token_hash, datetime.now())) - result = await cursor.fetchone() - - if result: - # Обновить время последнего использования - await db.execute(""" - UPDATE agent_tokens SET last_used = ? WHERE id = ? - """, (datetime.now(), result[0])) - await db.commit() - return True - return False - except Exception as e: - logger.error(f"Failed to verify token for agent {agent_id}: {e}") - return False - - async def revoke_agent_tokens(self, agent_id: str, token_type: Optional[str] = None) -> bool: - """Отозвать токены агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - if token_type: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? AND token_type = ? - """, (agent_id, token_type)) - else: - await db.execute(""" - UPDATE agent_tokens SET is_revoked = 1 - WHERE agent_id = ? - """, (agent_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to revoke tokens for agent {agent_id}: {e}") - return False - - async def create_agent_session(self, agent_id: str, session_id: str, - ip_address: str, expires_at: datetime, - user_agent: Optional[str] = None) -> bool: - """Создать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_sessions - (agent_id, session_id, ip_address, user_agent, expires_at) - VALUES (?, ?, ?, ?, ?) - """, (agent_id, session_id, ip_address, user_agent, expires_at)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to create session for agent {agent_id}: {e}") - return False - - async def update_agent_session_activity(self, session_id: str) -> bool: - """Обновить активность сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions - SET last_activity = ?, requests_count = requests_count + 1 - WHERE session_id = ? AND is_active = 1 - """, (datetime.now(), session_id)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to update session activity {session_id}: {e}") - return False - - async def get_active_agent_sessions(self, agent_id: str) -> List[Dict]: - """Получить активные сессии агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT session_id, ip_address, user_agent, created_at, - last_activity, requests_count - FROM agent_sessions - WHERE agent_id = ? AND is_active = 1 AND expires_at > ? - """, (agent_id, datetime.now())) - results = await cursor.fetchall() - - sessions = [] - for row in results: - sessions.append({ - 'session_id': row[0], - 'ip_address': row[1], - 'user_agent': row[2], - 'created_at': row[3], - 'last_activity': row[4], - 'requests_count': row[5] - }) - return sessions - except Exception as e: - logger.error(f"Failed to get sessions for agent {agent_id}: {e}") - return [] - - async def deactivate_agent_session(self, session_id: str) -> bool: - """Деактивировать сессию агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agent_sessions SET is_active = 0 WHERE session_id = ? - """, (session_id,)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to deactivate session {session_id}: {e}") - return False - - async def log_agent_auth_event(self, agent_id: str, ip_address: str, - action: str, success: bool, - error_message: Optional[str] = None, - user_agent: Optional[str] = None) -> bool: - """Записать событие аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO agent_auth_logs - (agent_id, ip_address, action, success, error_message, user_agent) - VALUES (?, ?, ?, ?, ?, ?) - """, (agent_id, ip_address, action, success, error_message, user_agent)) - await db.commit() - return True - except Exception as e: - logger.error(f"Failed to log auth event for agent {agent_id}: {e}") - return False - - async def get_agent_auth_logs(self, agent_id: str, limit: int = 100) -> List[Dict]: - """Получить логи аутентификации агента""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - SELECT ip_address, action, success, error_message, - user_agent, timestamp - FROM agent_auth_logs - WHERE agent_id = ? - ORDER BY timestamp DESC LIMIT ? - """, (agent_id, limit)) - results = await cursor.fetchall() - - logs = [] - for row in results: - logs.append({ - 'ip_address': row[0], - 'action': row[1], - 'success': bool(row[2]), - 'error_message': row[3], - 'user_agent': row[4], - 'timestamp': row[5] - }) - return logs - except Exception as e: - logger.error(f"Failed to get auth logs for agent {agent_id}: {e}") - return [] - - async def cleanup_expired_tokens(self) -> int: - """Очистка истекших токенов""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - DELETE FROM agent_tokens WHERE expires_at < ? - """, (datetime.now(),)) - await db.commit() - deleted_count = cursor.rowcount - logger.info(f"Cleaned up {deleted_count} expired tokens") - return deleted_count - except Exception as e: - logger.error(f"Failed to cleanup expired tokens: {e}") - return 0 - - async def cleanup_expired_sessions(self) -> int: - """Очистка истекших сессий""" - try: - async with aiosqlite.connect(self.db_path) as db: - cursor = await db.execute(""" - UPDATE agent_sessions SET is_active = 0 - WHERE expires_at < ? AND is_active = 1 - """, (datetime.now(),)) - await db.commit() - cleaned_count = cursor.rowcount - logger.info(f"Cleaned up {cleaned_count} expired sessions") - return cleaned_count - except Exception as e: - logger.error(f"Failed to cleanup expired sessions: {e}") - return 0 - - async def add_attack_attempt(self, ip: str, username: str, - attack_type: str, log_line: str, - timestamp: Optional[datetime] = None) -> None: - """Добавить попытку атаки в базу данных""" - if timestamp is None: - timestamp = datetime.now() - - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO attack_attempts - (ip_address, username, attack_type, timestamp, log_line) - VALUES (?, ?, ?, ?, ?) - """, (ip, username, attack_type, timestamp, log_line)) - await db.commit() - - async def get_attack_count_for_ip(self, ip: str, time_window: int) -> int: - """Получить количество попыток атак от IP за указанный период""" - since_time = datetime.now() - timedelta(seconds=time_window) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE ip_address = ? AND timestamp > ? - """, (ip, since_time)) as cursor: - result = await cursor.fetchone() - return result[0] if result else 0 - - async def ban_ip(self, ip: str, reason: str, unban_time: int, - manual: bool = False, attempts_count: int = 0) -> bool: - """Забанить IP адрес""" - unban_at = datetime.now() + timedelta(seconds=unban_time) - - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - INSERT OR REPLACE INTO banned_ips - (ip_address, ban_reason, unban_at, manual_ban, attempts_count) - VALUES (?, ?, ?, ?, ?) - """, (ip, reason, unban_at, manual, attempts_count)) - await db.commit() - logger.info(f"IP {ip} забанен. Причина: {reason}") - return True - except Exception as e: - logger.error(f"Ошибка при бане IP {ip}: {e}") - return False - - async def unban_ip(self, ip: str) -> bool: - """Разбанить IP адрес""" - async with aiosqlite.connect(self.db_path) as db: - try: - await db.execute(""" - UPDATE banned_ips - SET is_active = 0 - WHERE ip_address = ? AND is_active = 1 - """, (ip,)) - await db.commit() - logger.info(f"IP {ip} разбанен") - return True - except Exception as e: - logger.error(f"Ошибка при разбане IP {ip}: {e}") - return False - - async def is_ip_banned(self, ip: str) -> bool: - """Проверить, забанен ли IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT id FROM banned_ips - WHERE ip_address = ? AND is_active = 1 - AND (unban_at IS NULL OR unban_at > datetime('now')) - """, (ip,)) as cursor: - result = await cursor.fetchone() - return result is not None - - async def get_expired_bans(self) -> List[str]: - """Получить список IP с истекшим временем бана""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address FROM banned_ips - WHERE is_active = 1 AND unban_at <= datetime('now') - AND manual_ban = 0 - """) as cursor: - results = await cursor.fetchall() - return [row[0] for row in results] - - async def get_banned_ips(self) -> List[Dict]: - """Получить список всех забаненных IP""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, ban_reason, banned_at, unban_at, - manual_ban, attempts_count - FROM banned_ips - WHERE is_active = 1 - ORDER BY banned_at DESC - """) as cursor: - results = await cursor.fetchall() - - banned_list = [] - for row in results: - banned_list.append({ - 'ip': row[0], - 'reason': row[1], - 'banned_at': row[2], - 'unban_at': row[3], - 'manual': bool(row[4]), - 'attempts': row[5] - }) - return banned_list - - async def get_top_attackers(self, limit: int = 10, - days: int = 1) -> List[Dict]: - """Получить топ атакующих IP за указанный период""" - since_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, COUNT(*) as attempts, - MIN(timestamp) as first_attempt, - MAX(timestamp) as last_attempt, - GROUP_CONCAT(DISTINCT attack_type) as attack_types - FROM attack_attempts - WHERE timestamp > ? - GROUP BY ip_address - ORDER BY attempts DESC - LIMIT ? - """, (since_date, limit)) as cursor: - results = await cursor.fetchall() - - attackers = [] - for row in results: - attackers.append({ - 'ip': row[0], - 'attempts': row[1], - 'first_attempt': row[2], - 'last_attempt': row[3], - 'attack_types': row[4].split(',') if row[4] else [] - }) - return attackers - - async def get_ip_details(self, ip: str) -> Dict: - """Получить детальную информацию по IP""" - async with aiosqlite.connect(self.db_path) as db: - # Общая статистика по попыткам - async with db.execute(""" - SELECT COUNT(*) as total_attempts, - MIN(timestamp) as first_seen, - MAX(timestamp) as last_seen, - GROUP_CONCAT(DISTINCT attack_type) as attack_types, - GROUP_CONCAT(DISTINCT username) as usernames - FROM attack_attempts - WHERE ip_address = ? - """, (ip,)) as cursor: - attack_stats = await cursor.fetchone() - - # Информация о бане - async with db.execute(""" - SELECT ban_reason, banned_at, unban_at, is_active, manual_ban - FROM banned_ips - WHERE ip_address = ? - ORDER BY banned_at DESC - LIMIT 1 - """, (ip,)) as cursor: - ban_info = await cursor.fetchone() - - # Последние попытки - async with db.execute(""" - SELECT timestamp, attack_type, username, log_line - FROM attack_attempts - WHERE ip_address = ? - ORDER BY timestamp DESC - LIMIT 10 - """, (ip,)) as cursor: - recent_attempts = await cursor.fetchall() - - return { - 'ip': ip, - 'total_attempts': attack_stats[0] if attack_stats[0] else 0, - 'first_seen': attack_stats[1], - 'last_seen': attack_stats[2], - 'attack_types': attack_stats[3].split(',') if attack_stats[3] else [], - 'usernames': attack_stats[4].split(',') if attack_stats[4] else [], - 'is_banned': ban_info is not None and ban_info[3] == 1, - 'ban_info': { - 'reason': ban_info[0] if ban_info else None, - 'banned_at': ban_info[1] if ban_info else None, - 'unban_at': ban_info[2] if ban_info else None, - 'manual': bool(ban_info[4]) if ban_info else False - } if ban_info else None, - 'recent_attempts': [ - { - 'timestamp': attempt[0], - 'type': attempt[1], - 'username': attempt[2], - 'log_line': attempt[3] - } - for attempt in recent_attempts - ] - } - - async def add_successful_login(self, ip: str, username: str, - session_info: Optional[str] = None) -> None: - """Добавить запись об успешном входе""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO successful_logins - (ip_address, username, session_info) - VALUES (?, ?, ?) - """, (ip, username, session_info)) - await db.commit() - - async def get_daily_stats(self) -> Dict: - """Получить статистику за сегодня""" - today = datetime.now().date() - yesterday = today - timedelta(days=1) - - async with aiosqlite.connect(self.db_path) as db: - # Атаки за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_attacks = (await cursor.fetchone())[0] - - # Уникальные IP за сегодня - async with db.execute(""" - SELECT COUNT(DISTINCT ip_address) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_unique_ips = (await cursor.fetchone())[0] - - # Активные баны - async with db.execute(""" - SELECT COUNT(*) FROM banned_ips - WHERE is_active = 1 - """) as cursor: - active_bans = (await cursor.fetchone())[0] - - # Успешные входы за сегодня - async with db.execute(""" - SELECT COUNT(*) FROM successful_logins - WHERE DATE(timestamp) = ? - """, (today,)) as cursor: - today_logins = (await cursor.fetchone())[0] - - # Сравнение с вчера - async with db.execute(""" - SELECT COUNT(*) FROM attack_attempts - WHERE DATE(timestamp) = ? - """, (yesterday,)) as cursor: - yesterday_attacks = (await cursor.fetchone())[0] - - return { - 'today': { - 'attacks': today_attacks, - 'unique_ips': today_unique_ips, - 'successful_logins': today_logins - }, - 'yesterday': { - 'attacks': yesterday_attacks - }, - 'active_bans': active_bans, - 'attack_change': today_attacks - yesterday_attacks - } - - async def cleanup_old_records(self, days: int = 7) -> int: - """Очистка старых записей""" - cutoff_date = datetime.now() - timedelta(days=days) - - async with aiosqlite.connect(self.db_path) as db: - # Удаляем старые попытки атак - async with db.execute(""" - DELETE FROM attack_attempts - WHERE timestamp < ? - """, (cutoff_date,)) as cursor: - deleted_attempts = cursor.rowcount - - # Удаляем неактивные баны старше cutoff_date - await db.execute(""" - DELETE FROM banned_ips - WHERE is_active = 0 AND banned_at < ? - """, (cutoff_date,)) - - await db.commit() - logger.info(f"Очищено {deleted_attempts} старых записей") - return deleted_attempts - - async def is_whitelisted(self, ip: str, whitelist: List[str]) -> bool: - """Проверка IP в белом списке (поддержка CIDR)""" - try: - ip_obj = ipaddress.ip_address(ip) - for white_ip in whitelist: - try: - # Проверяем как сеть (CIDR) - if '/' in white_ip: - network = ipaddress.ip_network(white_ip, strict=False) - if ip_obj in network: - return True - # Проверяем как отдельный IP - elif ip_obj == ipaddress.ip_address(white_ip): - return True - except Exception: - continue - return False - except Exception: - return False - - async def record_compromise(self, compromise_info: Dict) -> None: - """Запись информации о компрометации""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT INTO compromises - (ip_address, username, detection_time, session_active, new_password, session_info) - VALUES (?, ?, ?, ?, ?, ?) - """, ( - compromise_info['ip'], - compromise_info['username'], - compromise_info['detection_time'], - compromise_info['session_active'], - compromise_info.get('new_password', ''), - json.dumps(compromise_info.get('sessions', [])) - )) - await db.commit() - - async def get_compromises(self, limit: int = 50) -> List[Dict]: - """Получение списка компрометаций""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT ip_address, username, detection_time, session_active, new_password, session_info - FROM compromises - ORDER BY detection_time DESC - LIMIT ? - """, (limit,)) as cursor: - results = await cursor.fetchall() - - compromises = [] - for row in results: - compromises.append({ - 'ip': row[0], - 'username': row[1], - 'detection_time': row[2], - 'session_active': bool(row[3]), - 'new_password': row[4], - 'sessions': json.loads(row[5]) if row[5] else [] - }) - return compromises - - async def update_daily_stats(self) -> None: - """Обновить ежедневную статистику""" - today = datetime.now().date() - - async with aiosqlite.connect(self.db_path) as db: - # Получаем статистику за сегодня - stats = await self.get_daily_stats() - - # Обновляем или создаем запись - await db.execute(""" - INSERT OR REPLACE INTO daily_stats - (date, total_attempts, unique_ips, successful_logins) - VALUES (?, ?, ?, ?) - """, (today, stats['today']['attacks'], - stats['today']['unique_ips'], - stats['today']['successful_logins'])) - await db.commit() - - # === CLUSTER MANAGEMENT METHODS === - - async def add_agent(self, agent_id: str, agent_info: Dict) -> None: - """Добавление агента в базу данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - INSERT OR REPLACE INTO agents - (agent_id, hostname, ip_address, ssh_port, ssh_user, status, added_time, last_check, version, config) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - """, ( - agent_id, - agent_info.get('hostname'), - agent_info.get('ip_address'), - agent_info.get('ssh_port', 22), - agent_info.get('ssh_user', 'root'), - agent_info.get('status', 'added'), - datetime.now().isoformat(), - agent_info.get('last_check'), - agent_info.get('version'), - json.dumps(agent_info) - )) - await db.commit() - - async def update_agent_status(self, agent_id: str, status: str) -> None: - """Обновление статуса агента""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute(""" - UPDATE agents - SET status = ?, last_check = ? - WHERE agent_id = ? - """, (status, datetime.now().isoformat(), agent_id)) - await db.commit() - - async def remove_agent(self, agent_id: str) -> None: - """Удаление агента из базы данных""" - async with aiosqlite.connect(self.db_path) as db: - await db.execute("DELETE FROM agents WHERE agent_id = ?", (agent_id,)) - await db.commit() - - async def get_agents(self) -> List[Dict]: - """Получение списка всех агентов""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, status, added_time, last_check, version - FROM agents - ORDER BY added_time DESC - """) as cursor: - results = await cursor.fetchall() - - agents = [] - for row in results: - agents.append({ - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'status': row[3], - 'added_time': row[4], - 'last_check': row[5], - 'version': row[6] - }) - - return agents - - async def get_agent_info(self, agent_id: str) -> Optional[Dict]: - """Получение информации об агенте""" - async with aiosqlite.connect(self.db_path) as db: - async with db.execute(""" - SELECT agent_id, hostname, ip_address, ssh_port, ssh_user, status, - added_time, last_check, version, config - FROM agents - WHERE agent_id = ? - """, (agent_id,)) as cursor: - row = await cursor.fetchone() - - if row: - config = json.loads(row[9]) if row[9] else {} - return { - 'agent_id': row[0], - 'hostname': row[1], - 'ip_address': row[2], - 'ssh_port': row[3], - 'ssh_user': row[4], - 'status': row[5], - 'added_time': row[6], - 'last_check': row[7], - 'version': row[8], - 'config': config - } - - return None - - async def get_cluster_stats(self) -> Dict: - """Получение статистики кластера""" - async with aiosqlite.connect(self.db_path) as db: - # Подсчет агентов по статусам - async with db.execute(""" - SELECT status, COUNT(*) - FROM agents - GROUP BY status - """) as cursor: - status_counts = {} - async for row in cursor: - status_counts[row[0]] = row[1] - - # Общее количество агентов - async with db.execute("SELECT COUNT(*) FROM agents") as cursor: - total_agents = (await cursor.fetchone())[0] - - return { - 'total_agents': total_agents, - 'status_distribution': status_counts, - 'online_agents': status_counts.get('online', 0), - 'offline_agents': status_counts.get('offline', 0), - 'deployed_agents': status_counts.get('deployed', 0) - } \ No newline at end of file diff --git a/.history/systemd/pyguardian_20251125195140.service b/.history/systemd/pyguardian_20251125195140.service deleted file mode 100644 index 483df1b..0000000 --- a/.history/systemd/pyguardian_20251125195140.service +++ /dev/null @@ -1,58 +0,0 @@ -[Unit] -Description=PyGuardian - Linux Server Protection System -Documentation=https://github.com/your-org/pyguardian -After=network.target network-online.target -Wants=network-online.target -RequiresMountsFor=/var/log /var/lib - -[Service] -Type=exec -User=root -Group=root - -# Рабочая директория -WorkingDirectory=/opt/pyguardian - -# Команда запуска -ExecStart=/usr/bin/python3 /opt/pyguardian/main.py /opt/pyguardian/config/config.yaml - -# Перезапуск при падении -Restart=always -RestartSec=10 -StartLimitInterval=0 - -# Переменные окружения -Environment=PYTHONPATH=/opt/pyguardian -Environment=PYTHONUNBUFFERED=1 - -# Ограничения ресурсов -MemoryLimit=256M -TasksMax=50 - -# Безопасность -NoNewPrivileges=false -ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/var/log /var/lib/pyguardian /tmp -PrivateTmp=true -PrivateDevices=false -ProtectKernelTunables=true -ProtectKernelModules=true -ProtectControlGroups=true - -# Capabilities для работы с firewall -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_DAC_READ_SEARCH -AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_DAC_READ_SEARCH - -# Стандартные потоки -StandardOutput=journal -StandardError=journal -SyslogIdentifier=pyguardian - -# Graceful shutdown -KillMode=mixed -KillSignal=SIGTERM -TimeoutStopSec=30 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/.history/systemd/pyguardian_20251125202055.service b/.history/systemd/pyguardian_20251125202055.service deleted file mode 100644 index 483df1b..0000000 --- a/.history/systemd/pyguardian_20251125202055.service +++ /dev/null @@ -1,58 +0,0 @@ -[Unit] -Description=PyGuardian - Linux Server Protection System -Documentation=https://github.com/your-org/pyguardian -After=network.target network-online.target -Wants=network-online.target -RequiresMountsFor=/var/log /var/lib - -[Service] -Type=exec -User=root -Group=root - -# Рабочая директория -WorkingDirectory=/opt/pyguardian - -# Команда запуска -ExecStart=/usr/bin/python3 /opt/pyguardian/main.py /opt/pyguardian/config/config.yaml - -# Перезапуск при падении -Restart=always -RestartSec=10 -StartLimitInterval=0 - -# Переменные окружения -Environment=PYTHONPATH=/opt/pyguardian -Environment=PYTHONUNBUFFERED=1 - -# Ограничения ресурсов -MemoryLimit=256M -TasksMax=50 - -# Безопасность -NoNewPrivileges=false -ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/var/log /var/lib/pyguardian /tmp -PrivateTmp=true -PrivateDevices=false -ProtectKernelTunables=true -ProtectKernelModules=true -ProtectControlGroups=true - -# Capabilities для работы с firewall -CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_DAC_READ_SEARCH -AmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_DAC_READ_SEARCH - -# Стандартные потоки -StandardOutput=journal -StandardError=journal -SyslogIdentifier=pyguardian - -# Graceful shutdown -KillMode=mixed -KillSignal=SIGTERM -TimeoutStopSec=30 - -[Install] -WantedBy=multi-user.target \ No newline at end of file diff --git a/.history/test_pyguardian_20251125195421.py b/.history/test_pyguardian_20251125195421.py deleted file mode 100644 index 6e563c7..0000000 --- a/.history/test_pyguardian_20251125195421.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 -""" -PyGuardian Test Script -Скрипт для тестирования компонентов системы -""" - -import asyncio -import sys -import os -from pathlib import Path - -# Добавляем src в путь для импортов -sys.path.insert(0, str(Path(__file__).parent / "src")) - -from src.storage import Storage -from src.monitor import LogParser, LogEvent -from datetime import datetime - - -async def test_storage(): - """Тест модуля хранения данных""" - print("🧪 Тестирование Storage...") - - # Создаем временную базу данных - db_path = "/tmp/test_guardian.db" - if os.path.exists(db_path): - os.remove(db_path) - - storage = Storage(db_path) - await storage.init_database() - - # Тест добавления попыток атак - await storage.add_attack_attempt( - ip="192.168.1.100", - username="root", - attack_type="failed_password", - log_line="Failed password for root from 192.168.1.100", - timestamp=datetime.now() - ) - - # Тест получения количества попыток - count = await storage.get_attack_count_for_ip("192.168.1.100", 60) - print(f"✅ Попыток атак от 192.168.1.100: {count}") - - # Тест бана IP - success = await storage.ban_ip("192.168.1.100", "Test ban", 3600) - print(f"✅ Бан IP: {'успешно' if success else 'ошибка'}") - - # Тест проверки бана - is_banned = await storage.is_ip_banned("192.168.1.100") - print(f"✅ IP забанен: {is_banned}") - - # Тест получения топ атакующих - top_attackers = await storage.get_top_attackers(limit=5) - print(f"✅ Топ атакующих: {len(top_attackers)} записей") - - # Очистка - os.remove(db_path) - print("✅ Storage тест завершен успешно\n") - - -def test_log_parser(): - """Тест парсера логов""" - print("🧪 Тестирование Log Parser...") - - # Создаем парсер - patterns = [ - "Failed password", - "Invalid user", - "authentication failure", - "Too many authentication failures" - ] - - parser = LogParser(patterns) - - # Тестовые строки логов - test_lines = [ - "Nov 25 14:30:15 server sshd[12345]: Failed password for root from 192.168.1.100 port 22 ssh2", - "Nov 25 14:31:15 server sshd[12348]: Invalid user hacker from 192.168.1.101 port 22", - "Nov 25 14:32:15 server sshd[12351]: Too many authentication failures for root from 192.168.1.102 port 22", - "Nov 25 14:34:15 server sshd[12353]: Accepted password for admin from 192.168.1.10 port 22 ssh2", - "Nov 25 14:35:15 server sshd[12355]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.1.104 user=root" - ] - - parsed_events = 0 - for line in test_lines: - event = parser.parse_line(line) - if event: - parsed_events += 1 - print(f" 📝 {event.event_type}: {event.ip_address} ({event.username})") - - print(f"✅ Распарсено {parsed_events} из {len(test_lines)} строк") - print("✅ Log Parser тест завершен успешно\n") - - -def test_whitelist(): - """Тест функции белого списка""" - print("🧪 Тестирование Whitelist...") - - storage = Storage(":memory:") # Временная база в памяти - - whitelist = [ - "127.0.0.1", - "192.168.1.0/24", - "10.0.0.0/8" - ] - - # Тестовые IP - test_cases = [ - ("127.0.0.1", True), # Localhost - ("192.168.1.50", True), # В сети 192.168.1.0/24 - ("10.5.10.20", True), # В сети 10.0.0.0/8 - ("8.8.8.8", False), # Публичный IP - ("192.168.2.10", False), # Другая подсеть - ] - - for ip, expected in test_cases: - # Используем синхронный цикл для теста - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - is_whitelisted = loop.run_until_complete( - storage.is_whitelisted(ip, whitelist) - ) - loop.close() - - status = "✅" if is_whitelisted == expected else "❌" - print(f" {status} {ip}: {'в белом списке' if is_whitelisted else 'не в белом списке'}") - - print("✅ Whitelist тест завершен успешно\n") - - -async def run_all_tests(): - """Запуск всех тестов""" - print("🚀 Запуск тестов PyGuardian\n") - - try: - await test_storage() - test_log_parser() - test_whitelist() - - print("🎉 Все тесты выполнены успешно!") - - except Exception as e: - print(f"❌ Ошибка в тестах: {e}") - return False - - return True - - -if __name__ == "__main__": - success = asyncio.run(run_all_tests()) - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/.history/test_pyguardian_20251125202055.py b/.history/test_pyguardian_20251125202055.py deleted file mode 100644 index 6e563c7..0000000 --- a/.history/test_pyguardian_20251125202055.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/env python3 -""" -PyGuardian Test Script -Скрипт для тестирования компонентов системы -""" - -import asyncio -import sys -import os -from pathlib import Path - -# Добавляем src в путь для импортов -sys.path.insert(0, str(Path(__file__).parent / "src")) - -from src.storage import Storage -from src.monitor import LogParser, LogEvent -from datetime import datetime - - -async def test_storage(): - """Тест модуля хранения данных""" - print("🧪 Тестирование Storage...") - - # Создаем временную базу данных - db_path = "/tmp/test_guardian.db" - if os.path.exists(db_path): - os.remove(db_path) - - storage = Storage(db_path) - await storage.init_database() - - # Тест добавления попыток атак - await storage.add_attack_attempt( - ip="192.168.1.100", - username="root", - attack_type="failed_password", - log_line="Failed password for root from 192.168.1.100", - timestamp=datetime.now() - ) - - # Тест получения количества попыток - count = await storage.get_attack_count_for_ip("192.168.1.100", 60) - print(f"✅ Попыток атак от 192.168.1.100: {count}") - - # Тест бана IP - success = await storage.ban_ip("192.168.1.100", "Test ban", 3600) - print(f"✅ Бан IP: {'успешно' if success else 'ошибка'}") - - # Тест проверки бана - is_banned = await storage.is_ip_banned("192.168.1.100") - print(f"✅ IP забанен: {is_banned}") - - # Тест получения топ атакующих - top_attackers = await storage.get_top_attackers(limit=5) - print(f"✅ Топ атакующих: {len(top_attackers)} записей") - - # Очистка - os.remove(db_path) - print("✅ Storage тест завершен успешно\n") - - -def test_log_parser(): - """Тест парсера логов""" - print("🧪 Тестирование Log Parser...") - - # Создаем парсер - patterns = [ - "Failed password", - "Invalid user", - "authentication failure", - "Too many authentication failures" - ] - - parser = LogParser(patterns) - - # Тестовые строки логов - test_lines = [ - "Nov 25 14:30:15 server sshd[12345]: Failed password for root from 192.168.1.100 port 22 ssh2", - "Nov 25 14:31:15 server sshd[12348]: Invalid user hacker from 192.168.1.101 port 22", - "Nov 25 14:32:15 server sshd[12351]: Too many authentication failures for root from 192.168.1.102 port 22", - "Nov 25 14:34:15 server sshd[12353]: Accepted password for admin from 192.168.1.10 port 22 ssh2", - "Nov 25 14:35:15 server sshd[12355]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=192.168.1.104 user=root" - ] - - parsed_events = 0 - for line in test_lines: - event = parser.parse_line(line) - if event: - parsed_events += 1 - print(f" 📝 {event.event_type}: {event.ip_address} ({event.username})") - - print(f"✅ Распарсено {parsed_events} из {len(test_lines)} строк") - print("✅ Log Parser тест завершен успешно\n") - - -def test_whitelist(): - """Тест функции белого списка""" - print("🧪 Тестирование Whitelist...") - - storage = Storage(":memory:") # Временная база в памяти - - whitelist = [ - "127.0.0.1", - "192.168.1.0/24", - "10.0.0.0/8" - ] - - # Тестовые IP - test_cases = [ - ("127.0.0.1", True), # Localhost - ("192.168.1.50", True), # В сети 192.168.1.0/24 - ("10.5.10.20", True), # В сети 10.0.0.0/8 - ("8.8.8.8", False), # Публичный IP - ("192.168.2.10", False), # Другая подсеть - ] - - for ip, expected in test_cases: - # Используем синхронный цикл для теста - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - is_whitelisted = loop.run_until_complete( - storage.is_whitelisted(ip, whitelist) - ) - loop.close() - - status = "✅" if is_whitelisted == expected else "❌" - print(f" {status} {ip}: {'в белом списке' if is_whitelisted else 'не в белом списке'}") - - print("✅ Whitelist тест завершен успешно\n") - - -async def run_all_tests(): - """Запуск всех тестов""" - print("🚀 Запуск тестов PyGuardian\n") - - try: - await test_storage() - test_log_parser() - test_whitelist() - - print("🎉 Все тесты выполнены успешно!") - - except Exception as e: - print(f"❌ Ошибка в тестах: {e}") - return False - - return True - - -if __name__ == "__main__": - success = asyncio.run(run_all_tests()) - sys.exit(0 if success else 1) \ No newline at end of file diff --git a/.history/tests/e2e/test_e2e_workflows_20251125211421.py b/.history/tests/e2e/test_e2e_workflows_20251125211421.py deleted file mode 100644 index d4895d7..0000000 --- a/.history/tests/e2e/test_e2e_workflows_20251125211421.py +++ /dev/null @@ -1,396 +0,0 @@ -#!/usr/bin/env python3 -""" -End-to-end tests for PyGuardian system. -""" - -import unittest -import tempfile -import os -import sys -import json -import time -import subprocess -import requests -from datetime import datetime - -# Add src directory to path -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src')) - - -class TestE2EWorkflow(unittest.TestCase): - """End-to-end workflow tests.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - self.test_config = { - 'api_host': 'localhost', - 'api_port': 8080, - 'agent_port': 8081 - } - - def tearDown(self): - """Clean up test fixtures.""" - os.rmdir(self.temp_dir) - - def test_agent_registration_workflow(self): - """Test complete agent registration workflow.""" - # Step 1: Agent requests registration - registration_data = { - 'agent_name': 'test-agent-e2e', - 'hostname': 'test-host.local', - 'ip_address': '192.168.1.100', - 'host_info': { - 'os': 'Linux', - 'arch': 'x86_64', - 'kernel': '5.4.0-74-generic' - } - } - - # Simulate registration request - self.assertIsNotNone(registration_data['agent_name']) - self.assertIsNotNone(registration_data['hostname']) - - # Step 2: Controller generates credentials - agent_id = f"agent_{registration_data['agent_name']}_123456" - credentials = { - 'agent_id': agent_id, - 'secret_key': 'generated_secret_key_here', - 'api_endpoint': f"https://{self.test_config['api_host']}:{self.test_config['api_port']}" - } - - self.assertEqual(credentials['agent_id'], agent_id) - self.assertIsNotNone(credentials['secret_key']) - - # Step 3: Agent receives credentials and authenticates - auth_request = { - 'agent_id': credentials['agent_id'], - 'secret_key': credentials['secret_key'] - } - - # Simulate authentication - jwt_token = "mocked.jwt.token.here" - self.assertIsNotNone(jwt_token) - - # Step 4: Agent makes authenticated API requests - api_request_headers = { - 'Authorization': f'Bearer {jwt_token}', - 'Content-Type': 'application/json' - } - - self.assertTrue(api_request_headers['Authorization'].startswith('Bearer ')) - - # Step 5: Verify agent appears in cluster - cluster_status = { - 'total_agents': 1, - 'active_agents': 1, - 'agents': [ - { - 'agent_id': agent_id, - 'status': 'active', - 'last_seen': datetime.now().isoformat(), - 'hostname': registration_data['hostname'] - } - ] - } - - self.assertEqual(cluster_status['total_agents'], 1) - self.assertEqual(cluster_status['active_agents'], 1) - self.assertEqual(cluster_status['agents'][0]['agent_id'], agent_id) - - def test_security_incident_workflow(self): - """Test security incident detection and response workflow.""" - # Step 1: Simulate security event detection - security_event = { - 'event_type': 'brute_force_attack', - 'source_ip': '203.0.113.100', - 'target_service': 'ssh', - 'attempts': 10, - 'timestamp': datetime.now().isoformat(), - 'severity': 'high' - } - - self.assertEqual(security_event['event_type'], 'brute_force_attack') - self.assertGreaterEqual(security_event['attempts'], 5) # Threshold - - # Step 2: Automatic threat analysis - threat_analysis = { - 'threat_level': 'high', - 'recommended_action': 'block_ip', - 'confidence': 0.95, - 'similar_events': 3 - } - - self.assertEqual(threat_analysis['threat_level'], 'high') - self.assertGreater(threat_analysis['confidence'], 0.8) - - # Step 3: Automatic response execution - response_action = { - 'action': 'ip_block', - 'target': security_event['source_ip'], - 'duration': 3600, # 1 hour - 'executed_at': datetime.now().isoformat(), - 'success': True - } - - self.assertEqual(response_action['action'], 'ip_block') - self.assertEqual(response_action['target'], security_event['source_ip']) - self.assertTrue(response_action['success']) - - # Step 4: Notification sent via Telegram - notification = { - 'type': 'security_alert', - 'message': f"🚨 Blocked {security_event['source_ip']} due to {security_event['event_type']}", - 'sent_at': datetime.now().isoformat(), - 'delivered': True - } - - self.assertEqual(notification['type'], 'security_alert') - self.assertIn(security_event['source_ip'], notification['message']) - self.assertTrue(notification['delivered']) - - # Step 5: Event logged for analysis - log_entry = { - 'event_id': 'evt_12345', - 'original_event': security_event, - 'analysis': threat_analysis, - 'response': response_action, - 'notification': notification, - 'logged_at': datetime.now().isoformat() - } - - self.assertIsNotNone(log_entry['event_id']) - self.assertIsNotNone(log_entry['original_event']) - self.assertIsNotNone(log_entry['response']) - - def test_cluster_health_monitoring(self): - """Test cluster health monitoring workflow.""" - # Step 1: Collect agent health data - agent_health_data = [ - { - 'agent_id': 'agent_web01_123456', - 'status': 'healthy', - 'cpu_usage': 45.2, - 'memory_usage': 62.8, - 'disk_usage': 78.1, - 'network_rx': 1024000, - 'network_tx': 2048000, - 'last_heartbeat': datetime.now().isoformat() - }, - { - 'agent_id': 'agent_db01_789012', - 'status': 'warning', - 'cpu_usage': 85.7, - 'memory_usage': 91.3, - 'disk_usage': 45.6, - 'network_rx': 512000, - 'network_tx': 1024000, - 'last_heartbeat': datetime.now().isoformat() - } - ] - - # Validate health data - for agent in agent_health_data: - self.assertIn('agent_id', agent) - self.assertIn('status', agent) - self.assertLessEqual(agent['cpu_usage'], 100) - self.assertLessEqual(agent['memory_usage'], 100) - self.assertLessEqual(agent['disk_usage'], 100) - - # Step 2: Analyze cluster health - cluster_health = { - 'total_agents': len(agent_health_data), - 'healthy_agents': len([a for a in agent_health_data if a['status'] == 'healthy']), - 'warning_agents': len([a for a in agent_health_data if a['status'] == 'warning']), - 'critical_agents': len([a for a in agent_health_data if a['status'] == 'critical']), - 'overall_status': 'warning', - 'average_cpu': sum(a['cpu_usage'] for a in agent_health_data) / len(agent_health_data), - 'average_memory': sum(a['memory_usage'] for a in agent_health_data) / len(agent_health_data) - } - - self.assertEqual(cluster_health['total_agents'], 2) - self.assertEqual(cluster_health['healthy_agents'], 1) - self.assertEqual(cluster_health['warning_agents'], 1) - self.assertLessEqual(cluster_health['average_cpu'], 100) - - # Step 3: Generate alerts for concerning metrics - alerts = [] - for agent in agent_health_data: - if agent['cpu_usage'] > 80: - alerts.append({ - 'type': 'high_cpu', - 'agent_id': agent['agent_id'], - 'value': agent['cpu_usage'], - 'threshold': 80 - }) - if agent['memory_usage'] > 90: - alerts.append({ - 'type': 'high_memory', - 'agent_id': agent['agent_id'], - 'value': agent['memory_usage'], - 'threshold': 90 - }) - - # Verify alerts were generated - self.assertGreater(len(alerts), 0) - cpu_alerts = [a for a in alerts if a['type'] == 'high_cpu'] - memory_alerts = [a for a in alerts if a['type'] == 'high_memory'] - - self.assertEqual(len(cpu_alerts), 1) - self.assertEqual(len(memory_alerts), 1) - - def test_backup_and_recovery(self): - """Test backup and recovery workflow.""" - # Step 1: Create backup - backup_data = { - 'backup_id': 'backup_20241125_123456', - 'created_at': datetime.now().isoformat(), - 'backup_type': 'full', - 'components': [ - 'configuration', - 'agent_credentials', - 'security_logs', - 'cluster_state' - ], - 'size_bytes': 1024000, - 'compressed': True - } - - self.assertIsNotNone(backup_data['backup_id']) - self.assertEqual(backup_data['backup_type'], 'full') - self.assertIn('agent_credentials', backup_data['components']) - - # Step 2: Verify backup integrity - integrity_check = { - 'backup_id': backup_data['backup_id'], - 'checksum': 'sha256_checksum_here', - 'verification_passed': True, - 'verified_at': datetime.now().isoformat() - } - - self.assertTrue(integrity_check['verification_passed']) - self.assertIsNotNone(integrity_check['checksum']) - - # Step 3: Simulate recovery scenario - recovery_scenario = { - 'scenario': 'controller_failure', - 'recovery_method': 'restore_from_backup', - 'backup_used': backup_data['backup_id'], - 'recovery_time': 300, # seconds - 'success': True - } - - self.assertEqual(recovery_scenario['recovery_method'], 'restore_from_backup') - self.assertTrue(recovery_scenario['success']) - self.assertLess(recovery_scenario['recovery_time'], 600) # Under 10 minutes - - -class TestPerformance(unittest.TestCase): - """Performance and load tests.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - """Clean up test fixtures.""" - os.rmdir(self.temp_dir) - - def test_concurrent_agent_authentication(self): - """Test concurrent agent authentication performance.""" - # Simulate multiple agents authenticating simultaneously - concurrent_agents = 50 - authentication_times = [] - - for i in range(concurrent_agents): - # Simulate authentication time - start_time = time.time() - - # Mock authentication process - agent_id = f"agent_load_test_{i:03d}" - auth_result = { - 'agent_id': agent_id, - 'authenticated': True, - 'token_generated': True - } - - end_time = time.time() - auth_time = end_time - start_time - authentication_times.append(auth_time) - - self.assertTrue(auth_result['authenticated']) - - # Analyze performance - avg_auth_time = sum(authentication_times) / len(authentication_times) - max_auth_time = max(authentication_times) - - # Performance assertions - self.assertLess(avg_auth_time, 1.0) # Average under 1 second - self.assertLess(max_auth_time, 5.0) # Maximum under 5 seconds - self.assertEqual(len(authentication_times), concurrent_agents) - - def test_api_throughput(self): - """Test API request throughput.""" - # Simulate high-frequency API requests - total_requests = 1000 - successful_requests = 0 - failed_requests = 0 - - start_time = time.time() - - for i in range(total_requests): - # Simulate API request processing - request_success = True # Mock success - - if request_success: - successful_requests += 1 - else: - failed_requests += 1 - - end_time = time.time() - total_time = end_time - start_time - - # Calculate throughput - requests_per_second = total_requests / total_time if total_time > 0 else 0 - success_rate = successful_requests / total_requests - - # Performance assertions - self.assertGreater(requests_per_second, 100) # At least 100 RPS - self.assertGreaterEqual(success_rate, 0.95) # 95% success rate - self.assertEqual(successful_requests + failed_requests, total_requests) - - -def run_e2e_tests(): - """Run all end-to-end tests.""" - print("🎯 Running PyGuardian End-to-End Tests...") - print("=" * 50) - - # Create test suite - test_suite = unittest.TestSuite() - - # Add test classes - test_classes = [ - TestE2EWorkflow, - TestPerformance - ] - - for test_class in test_classes: - tests = unittest.TestLoader().loadTestsFromTestCase(test_class) - test_suite.addTests(tests) - - # Run tests - runner = unittest.TextTestRunner(verbosity=2) - result = runner.run(test_suite) - - # Print summary - print("\n" + "=" * 50) - print(f"🏁 E2E Tests completed:") - print(f" ✅ Passed: {result.testsRun - len(result.failures) - len(result.errors)}") - print(f" ❌ Failed: {len(result.failures)}") - print(f" 💥 Errors: {len(result.errors)}") - - return 0 if result.wasSuccessful() else 1 - - -if __name__ == '__main__': - sys.exit(run_e2e_tests()) \ No newline at end of file diff --git a/.history/tests/integration/test_api_integration_20251125211329.py b/.history/tests/integration/test_api_integration_20251125211329.py deleted file mode 100644 index 11b881d..0000000 --- a/.history/tests/integration/test_api_integration_20251125211329.py +++ /dev/null @@ -1,391 +0,0 @@ -#!/usr/bin/env python3 -""" -Integration tests for PyGuardian API and cluster management. -""" - -import unittest -import tempfile -import os -import sys -import json -import asyncio -import aiohttp -from unittest.mock import Mock, patch, AsyncMock -import sqlite3 -from datetime import datetime - -# Add src directory to path -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src')) - - -class TestAPIServer(unittest.TestCase): - """Integration tests for API server.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - self.db_path = os.path.join(self.temp_dir, 'test_guardian.db') - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.db_path): - os.remove(self.db_path) - os.rmdir(self.temp_dir) - - def test_api_health_endpoint(self): - """Test API health check endpoint.""" - # This would be an actual HTTP test - # For now, just test that we can import the module - try: - from api_server import PyGuardianAPI - self.assertTrue(True) - except ImportError: - self.fail("Could not import API server module") - - def test_agent_registration_flow(self): - """Test agent registration API flow.""" - # Mock test for agent registration - test_data = { - 'agent_name': 'test_agent', - 'host_info': { - 'hostname': 'test-host', - 'os': 'linux', - 'arch': 'x86_64' - } - } - - # This would test the actual API endpoint - self.assertIsNotNone(test_data) - - def test_jwt_authentication_middleware(self): - """Test JWT authentication middleware.""" - # Test JWT authentication in API requests - test_token = "Bearer test.jwt.token" - - # Mock authorization header validation - self.assertTrue(test_token.startswith("Bearer ")) - - -class TestClusterManager(unittest.TestCase): - """Integration tests for cluster management.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - """Clean up test fixtures.""" - os.rmdir(self.temp_dir) - - def test_cluster_manager_import(self): - """Test cluster manager module import.""" - try: - from cluster_manager import ClusterManager - self.assertTrue(True) - except ImportError: - self.fail("Could not import ClusterManager") - - def test_agent_registration(self): - """Test agent registration in cluster.""" - # Mock agent registration - agent_data = { - 'agent_id': 'agent_test123', - 'hostname': 'test-agent', - 'ip_address': '192.168.1.100', - 'status': 'active' - } - - self.assertEqual(agent_data['status'], 'active') - - def test_agent_health_check(self): - """Test agent health monitoring.""" - # Mock health check - health_data = { - 'agent_id': 'agent_test123', - 'last_seen': datetime.now().isoformat(), - 'status': 'healthy', - 'cpu_usage': 25.5, - 'memory_usage': 60.2, - 'disk_usage': 45.0 - } - - self.assertEqual(health_data['status'], 'healthy') - self.assertLess(health_data['cpu_usage'], 100) - self.assertLess(health_data['memory_usage'], 100) - - -class TestTelegramBot(unittest.TestCase): - """Integration tests for Telegram bot.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - """Clean up test fixtures.""" - os.rmdir(self.temp_dir) - - def test_bot_import(self): - """Test Telegram bot module import.""" - try: - from bot import TelegramBot - self.assertTrue(True) - except ImportError: - self.fail("Could not import TelegramBot") - - def test_command_parsing(self): - """Test bot command parsing.""" - # Mock command parsing - test_commands = [ - '/start', - '/status', - '/cluster', - '/agents', - '/help' - ] - - for cmd in test_commands: - self.assertTrue(cmd.startswith('/')) - - def test_authentication_commands(self): - """Test authentication-related bot commands.""" - # Mock authentication commands - auth_commands = [ - '/generate_agent', - '/revoke_token', - '/list_agents', - '/agent_status' - ] - - for cmd in auth_commands: - self.assertTrue(isinstance(cmd, str)) - - -class TestSecurityMonitor(unittest.TestCase): - """Integration tests for security monitoring.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - """Clean up test fixtures.""" - os.rmdir(self.temp_dir) - - def test_security_monitor_import(self): - """Test security monitor import.""" - try: - from monitor import SecurityMonitor - self.assertTrue(True) - except ImportError: - self.fail("Could not import SecurityMonitor") - - def test_threat_detection(self): - """Test threat detection logic.""" - # Mock threat detection - threat_events = [ - { - 'type': 'brute_force', - 'source_ip': '192.168.1.100', - 'attempts': 5, - 'timestamp': datetime.now().isoformat() - }, - { - 'type': 'port_scan', - 'source_ip': '10.0.0.50', - 'ports': [22, 80, 443], - 'timestamp': datetime.now().isoformat() - } - ] - - for event in threat_events: - self.assertIn('type', event) - self.assertIn('source_ip', event) - self.assertIn('timestamp', event) - - -class TestFirewallManager(unittest.TestCase): - """Integration tests for firewall management.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - """Clean up test fixtures.""" - os.rmdir(self.temp_dir) - - def test_firewall_import(self): - """Test firewall module import.""" - try: - from firewall import FirewallManager - self.assertTrue(True) - except ImportError: - self.fail("Could not import FirewallManager") - - def test_ip_blocking(self): - """Test IP address blocking.""" - # Mock IP blocking - blocked_ips = [ - '192.168.1.100', - '10.0.0.50', - '203.0.113.1' - ] - - for ip in blocked_ips: - # Validate IP format (basic check) - parts = ip.split('.') - self.assertEqual(len(parts), 4) - for part in parts: - self.assertTrue(0 <= int(part) <= 255) - - def test_whitelist_management(self): - """Test IP whitelist management.""" - # Mock whitelist - whitelist = [ - '127.0.0.1', - '192.168.1.0/24', - '10.0.0.0/8' - ] - - for entry in whitelist: - self.assertIsInstance(entry, str) - self.assertTrue('.' in entry) - - -class TestDatabaseOperations(unittest.TestCase): - """Integration tests for database operations.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - self.db_path = os.path.join(self.temp_dir, 'test_integration.db') - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.db_path): - os.remove(self.db_path) - os.rmdir(self.temp_dir) - - def test_database_creation(self): - """Test database creation and schema.""" - # Create SQLite database - conn = sqlite3.connect(self.db_path) - cursor = conn.cursor() - - # Create a test table - cursor.execute(''' - CREATE TABLE test_agents ( - id INTEGER PRIMARY KEY, - agent_id TEXT UNIQUE, - status TEXT, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - ''') - - # Insert test data - cursor.execute(''' - INSERT INTO test_agents (agent_id, status) - VALUES (?, ?) - ''', ('agent_test123', 'active')) - - conn.commit() - - # Verify data - cursor.execute('SELECT * FROM test_agents') - results = cursor.fetchall() - - self.assertEqual(len(results), 1) - self.assertEqual(results[0][1], 'agent_test123') - self.assertEqual(results[0][2], 'active') - - conn.close() - - def test_agent_authentication_tables(self): - """Test agent authentication tables.""" - conn = sqlite3.connect(self.db_path) - cursor = conn.cursor() - - # Create authentication tables - cursor.execute(''' - CREATE TABLE agent_auth ( - id INTEGER PRIMARY KEY, - agent_id TEXT UNIQUE NOT NULL, - key_hash TEXT NOT NULL, - encrypted_key TEXT NOT NULL, - is_active BOOLEAN DEFAULT TRUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - ) - ''') - - cursor.execute(''' - CREATE TABLE agent_tokens ( - id INTEGER PRIMARY KEY, - agent_id TEXT NOT NULL, - token TEXT NOT NULL, - expires_at TIMESTAMP NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (agent_id) REFERENCES agent_auth (agent_id) - ) - ''') - - # Test data insertion - cursor.execute(''' - INSERT INTO agent_auth (agent_id, key_hash, encrypted_key) - VALUES (?, ?, ?) - ''', ('agent_test123', 'test_hash', 'encrypted_key')) - - conn.commit() - - # Verify tables exist and have data - cursor.execute("SELECT name FROM sqlite_master WHERE type='table'") - tables = [row[0] for row in cursor.fetchall()] - - self.assertIn('agent_auth', tables) - self.assertIn('agent_tokens', tables) - - cursor.execute('SELECT COUNT(*) FROM agent_auth') - count = cursor.fetchone()[0] - self.assertEqual(count, 1) - - conn.close() - - -def run_integration_tests(): - """Run all integration tests.""" - print("🔄 Running PyGuardian Integration Tests...") - print("=" * 50) - - # Create test suite - test_suite = unittest.TestSuite() - - # Add test classes - test_classes = [ - TestAPIServer, - TestClusterManager, - TestTelegramBot, - TestSecurityMonitor, - TestFirewallManager, - TestDatabaseOperations - ] - - for test_class in test_classes: - tests = unittest.TestLoader().loadTestsFromTestCase(test_class) - test_suite.addTests(tests) - - # Run tests - runner = unittest.TextTestRunner(verbosity=2) - result = runner.run(test_suite) - - # Print summary - print("\n" + "=" * 50) - print(f"🏁 Integration Tests completed:") - print(f" ✅ Passed: {result.testsRun - len(result.failures) - len(result.errors)}") - print(f" ❌ Failed: {len(result.failures)}") - print(f" 💥 Errors: {len(result.errors)}") - - return 0 if result.wasSuccessful() else 1 - - -if __name__ == '__main__': - sys.exit(run_integration_tests()) \ No newline at end of file diff --git a/.history/tests/run_tests_20251125211446.py b/.history/tests/run_tests_20251125211446.py deleted file mode 100644 index a946fcf..0000000 --- a/.history/tests/run_tests_20251125211446.py +++ /dev/null @@ -1,233 +0,0 @@ -#!/usr/bin/env python3 -""" -Test runner script for all PyGuardian tests. -""" - -import sys -import os -import subprocess -import time -from pathlib import Path - -def print_banner(): - """Print test banner.""" - print("=" * 60) - print("🧪 PyGuardian Test Suite Runner") - print("=" * 60) - -def run_unit_tests(): - """Run unit tests.""" - print("\n📝 Running Unit Tests...") - print("-" * 30) - - try: - # Run unit tests - result = subprocess.run([ - sys.executable, '-m', 'pytest', - 'tests/unit/', - '-v', '--tb=short' - ], capture_output=True, text=True, cwd=Path(__file__).parent.parent) - - print(result.stdout) - if result.stderr: - print("STDERR:", result.stderr) - - return result.returncode == 0 - except Exception as e: - print(f"❌ Unit tests failed: {e}") - return False - -def run_integration_tests(): - """Run integration tests.""" - print("\n🔄 Running Integration Tests...") - print("-" * 30) - - try: - result = subprocess.run([ - sys.executable, '-m', 'pytest', - 'tests/integration/', - '-v', '--tb=short' - ], capture_output=True, text=True, cwd=Path(__file__).parent.parent) - - print(result.stdout) - if result.stderr: - print("STDERR:", result.stderr) - - return result.returncode == 0 - except Exception as e: - print(f"❌ Integration tests failed: {e}") - return False - -def run_e2e_tests(): - """Run end-to-end tests.""" - print("\n🎯 Running End-to-End Tests...") - print("-" * 30) - - try: - result = subprocess.run([ - sys.executable, '-m', 'pytest', - 'tests/e2e/', - '-v', '--tb=short' - ], capture_output=True, text=True, cwd=Path(__file__).parent.parent) - - print(result.stdout) - if result.stderr: - print("STDERR:", result.stderr) - - return result.returncode == 0 - except Exception as e: - print(f"❌ E2E tests failed: {e}") - return False - -def run_coverage_report(): - """Generate coverage report.""" - print("\n📊 Generating Coverage Report...") - print("-" * 30) - - try: - # Run tests with coverage - result = subprocess.run([ - sys.executable, '-m', 'pytest', - '--cov=src', - '--cov-report=html', - '--cov-report=term-missing', - 'tests/' - ], capture_output=True, text=True, cwd=Path(__file__).parent.parent) - - print(result.stdout) - if result.stderr: - print("STDERR:", result.stderr) - - return result.returncode == 0 - except Exception as e: - print(f"❌ Coverage report failed: {e}") - return False - -def run_linting(): - """Run code linting.""" - print("\n🔍 Running Code Linting...") - print("-" * 30) - - try: - # Run flake8 linting - result = subprocess.run([ - sys.executable, '-m', 'flake8', - 'src/', 'tests/', - '--max-line-length=100', - '--ignore=E203,W503' - ], capture_output=True, text=True, cwd=Path(__file__).parent.parent) - - if result.stdout: - print("Linting issues found:") - print(result.stdout) - else: - print("✅ No linting issues found") - - return result.returncode == 0 - except Exception as e: - print(f"❌ Linting failed: {e}") - return False - -def check_dependencies(): - """Check if required dependencies are installed.""" - print("\n📦 Checking Dependencies...") - print("-" * 30) - - required_packages = [ - 'pytest', - 'pytest-cov', - 'flake8', - 'PyJWT', - 'cryptography' - ] - - missing_packages = [] - - for package in required_packages: - try: - __import__(package.replace('-', '_').lower()) - print(f"✅ {package}") - except ImportError: - print(f"❌ {package}") - missing_packages.append(package) - - if missing_packages: - print(f"\n⚠️ Missing packages: {', '.join(missing_packages)}") - print("Install with: pip install " + " ".join(missing_packages)) - return False - - return True - -def main(): - """Main test runner.""" - print_banner() - - start_time = time.time() - - # Check dependencies first - if not check_dependencies(): - print("\n❌ Dependency check failed. Please install missing packages.") - return 1 - - # Track results - results = { - 'unit': True, - 'integration': True, - 'e2e': True, - 'linting': True, - 'coverage': True - } - - # Run different test suites based on arguments - if len(sys.argv) > 1: - test_type = sys.argv[1] - if test_type == 'unit': - results['unit'] = run_unit_tests() - elif test_type == 'integration': - results['integration'] = run_integration_tests() - elif test_type == 'e2e': - results['e2e'] = run_e2e_tests() - elif test_type == 'lint': - results['linting'] = run_linting() - elif test_type == 'coverage': - results['coverage'] = run_coverage_report() - else: - print(f"Unknown test type: {test_type}") - print("Available types: unit, integration, e2e, lint, coverage") - return 1 - else: - # Run all tests - results['linting'] = run_linting() - results['unit'] = run_unit_tests() - results['integration'] = run_integration_tests() - results['e2e'] = run_e2e_tests() - results['coverage'] = run_coverage_report() - - # Print final summary - end_time = time.time() - duration = end_time - start_time - - print("\n" + "=" * 60) - print("📊 Test Summary") - print("=" * 60) - - total_tests = len(results) - passed_tests = sum(1 for result in results.values() if result) - failed_tests = total_tests - passed_tests - - for test_name, result in results.items(): - status = "✅ PASS" if result else "❌ FAIL" - print(f"{test_name.upper():12} {status}") - - print("-" * 60) - print(f"Total: {total_tests}") - print(f"Passed: {passed_tests}") - print(f"Failed: {failed_tests}") - print(f"Duration: {duration:.2f}s") - print("=" * 60) - - # Return appropriate exit code - return 0 if all(results.values()) else 1 - -if __name__ == '__main__': - sys.exit(main()) \ No newline at end of file diff --git a/.history/tests/unit/test_authentication_20251125211250.py b/.history/tests/unit/test_authentication_20251125211250.py deleted file mode 100644 index d7e49ba..0000000 --- a/.history/tests/unit/test_authentication_20251125211250.py +++ /dev/null @@ -1,421 +0,0 @@ -#!/usr/bin/env python3 -""" -Comprehensive unit tests for PyGuardian authentication system. -""" - -import unittest -import tempfile -import os -import sys -import sqlite3 -import jwt -import hashlib -import hmac -from datetime import datetime, timedelta -from unittest.mock import Mock, patch, MagicMock - -# Add src directory to path for imports -sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../src')) - -from auth import AgentAuthentication -from storage import Database - - -class TestAgentAuthentication(unittest.TestCase): - """Test cases for agent authentication system.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - self.db_path = os.path.join(self.temp_dir, 'test_guardian.db') - self.auth = AgentAuthentication() - - # Create test database - self.db = Database(self.db_path) - self.db.create_tables() - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.db_path): - os.remove(self.db_path) - os.rmdir(self.temp_dir) - - def test_generate_agent_id(self): - """Test agent ID generation.""" - agent_id = self.auth.generate_agent_id() - - # Check format - self.assertTrue(agent_id.startswith('agent_')) - self.assertEqual(len(agent_id), 42) # 'agent_' + 36 char UUID - - # Test uniqueness - agent_id2 = self.auth.generate_agent_id() - self.assertNotEqual(agent_id, agent_id2) - - def test_create_agent_credentials(self): - """Test agent credentials creation.""" - agent_id = self.auth.generate_agent_id() - credentials = self.auth.create_agent_credentials(agent_id) - - # Check required fields - required_fields = ['agent_id', 'secret_key', 'encrypted_key', 'key_hash'] - for field in required_fields: - self.assertIn(field, credentials) - - # Check agent ID matches - self.assertEqual(credentials['agent_id'], agent_id) - - # Check secret key length - self.assertEqual(len(credentials['secret_key']), 64) # 32 bytes hex encoded - - # Check key hash - expected_hash = hashlib.sha256(credentials['secret_key'].encode()).hexdigest() - self.assertEqual(credentials['key_hash'], expected_hash) - - def test_generate_jwt_token(self): - """Test JWT token generation.""" - agent_id = self.auth.generate_agent_id() - secret_key = self.auth._generate_secret_key() - - token = self.auth.generate_jwt_token(agent_id, secret_key) - - # Verify token structure - self.assertIsInstance(token, str) - self.assertTrue(len(token) > 100) # JWT tokens are typically long - - # Decode and verify payload - decoded = jwt.decode(token, secret_key, algorithms=['HS256']) - self.assertEqual(decoded['agent_id'], agent_id) - self.assertIn('iat', decoded) - self.assertIn('exp', decoded) - self.assertIn('jti', decoded) - - def test_verify_jwt_token_valid(self): - """Test JWT token verification with valid token.""" - agent_id = self.auth.generate_agent_id() - secret_key = self.auth._generate_secret_key() - token = self.auth.generate_jwt_token(agent_id, secret_key) - - is_valid = self.auth.verify_jwt_token(token, secret_key) - self.assertTrue(is_valid) - - def test_verify_jwt_token_invalid(self): - """Test JWT token verification with invalid token.""" - secret_key = self.auth._generate_secret_key() - - # Test with invalid token - is_valid = self.auth.verify_jwt_token("invalid.jwt.token", secret_key) - self.assertFalse(is_valid) - - # Test with wrong secret key - agent_id = self.auth.generate_agent_id() - token = self.auth.generate_jwt_token(agent_id, secret_key) - wrong_key = self.auth._generate_secret_key() - - is_valid = self.auth.verify_jwt_token(token, wrong_key) - self.assertFalse(is_valid) - - def test_verify_jwt_token_expired(self): - """Test JWT token verification with expired token.""" - agent_id = self.auth.generate_agent_id() - secret_key = self.auth._generate_secret_key() - - # Create expired token - payload = { - 'agent_id': agent_id, - 'exp': datetime.utcnow() - timedelta(hours=1), # Expired 1 hour ago - 'iat': datetime.utcnow() - timedelta(hours=2), - 'jti': self.auth._generate_jti() - } - - expired_token = jwt.encode(payload, secret_key, algorithm='HS256') - - is_valid = self.auth.verify_jwt_token(expired_token, secret_key) - self.assertFalse(is_valid) - - def test_create_hmac_signature(self): - """Test HMAC signature creation.""" - data = "test message" - secret_key = self.auth._generate_secret_key() - - signature = self.auth.create_hmac_signature(data, secret_key) - - # Verify signature format - self.assertEqual(len(signature), 64) # SHA256 hex digest - - # Verify signature is correct - expected = hmac.new( - secret_key.encode(), - data.encode(), - hashlib.sha256 - ).hexdigest() - - self.assertEqual(signature, expected) - - def test_verify_hmac_signature_valid(self): - """Test HMAC signature verification with valid signature.""" - data = "test message" - secret_key = self.auth._generate_secret_key() - - signature = self.auth.create_hmac_signature(data, secret_key) - is_valid = self.auth.verify_hmac_signature(data, signature, secret_key) - - self.assertTrue(is_valid) - - def test_verify_hmac_signature_invalid(self): - """Test HMAC signature verification with invalid signature.""" - data = "test message" - secret_key = self.auth._generate_secret_key() - - # Test with wrong signature - wrong_signature = "0" * 64 - is_valid = self.auth.verify_hmac_signature(data, wrong_signature, secret_key) - self.assertFalse(is_valid) - - # Test with wrong key - signature = self.auth.create_hmac_signature(data, secret_key) - wrong_key = self.auth._generate_secret_key() - is_valid = self.auth.verify_hmac_signature(data, signature, wrong_key) - self.assertFalse(is_valid) - - def test_encrypt_decrypt_secret_key(self): - """Test secret key encryption and decryption.""" - secret_key = self.auth._generate_secret_key() - password = "test_password" - - encrypted = self.auth.encrypt_secret_key(secret_key, password) - decrypted = self.auth.decrypt_secret_key(encrypted, password) - - self.assertEqual(secret_key, decrypted) - - def test_encrypt_decrypt_wrong_password(self): - """Test secret key decryption with wrong password.""" - secret_key = self.auth._generate_secret_key() - password = "test_password" - wrong_password = "wrong_password" - - encrypted = self.auth.encrypt_secret_key(secret_key, password) - - with self.assertRaises(Exception): - self.auth.decrypt_secret_key(encrypted, wrong_password) - - @patch('src.auth.Database') - def test_authenticate_agent_success(self, mock_db_class): - """Test successful agent authentication.""" - # Mock database - mock_db = Mock() - mock_db_class.return_value = mock_db - - agent_id = self.auth.generate_agent_id() - secret_key = self.auth._generate_secret_key() - key_hash = hashlib.sha256(secret_key.encode()).hexdigest() - - # Mock database response - mock_db.get_agent_credentials.return_value = { - 'agent_id': agent_id, - 'key_hash': key_hash, - 'is_active': True, - 'created_at': datetime.now().isoformat() - } - - result = self.auth.authenticate_agent(agent_id, secret_key) - self.assertTrue(result) - - @patch('src.auth.Database') - def test_authenticate_agent_failure(self, mock_db_class): - """Test failed agent authentication.""" - # Mock database - mock_db = Mock() - mock_db_class.return_value = mock_db - - agent_id = self.auth.generate_agent_id() - secret_key = self.auth._generate_secret_key() - - # Mock database response - no credentials found - mock_db.get_agent_credentials.return_value = None - - result = self.auth.authenticate_agent(agent_id, secret_key) - self.assertFalse(result) - - -class TestDatabase(unittest.TestCase): - """Test cases for database operations.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - self.db_path = os.path.join(self.temp_dir, 'test_guardian.db') - self.db = Database(self.db_path) - self.db.create_tables() - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.db_path): - os.remove(self.db_path) - os.rmdir(self.temp_dir) - - def test_create_agent_auth(self): - """Test agent authentication record creation.""" - agent_id = "agent_test123" - secret_key_hash = "test_hash" - encrypted_key = "encrypted_test_key" - - success = self.db.create_agent_auth(agent_id, secret_key_hash, encrypted_key) - self.assertTrue(success) - - # Verify record exists - credentials = self.db.get_agent_credentials(agent_id) - self.assertIsNotNone(credentials) - self.assertEqual(credentials['agent_id'], agent_id) - self.assertEqual(credentials['key_hash'], secret_key_hash) - - def test_get_agent_credentials_exists(self): - """Test retrieving existing agent credentials.""" - agent_id = "agent_test123" - secret_key_hash = "test_hash" - encrypted_key = "encrypted_test_key" - - # Create record - self.db.create_agent_auth(agent_id, secret_key_hash, encrypted_key) - - # Retrieve record - credentials = self.db.get_agent_credentials(agent_id) - - self.assertIsNotNone(credentials) - self.assertEqual(credentials['agent_id'], agent_id) - self.assertEqual(credentials['key_hash'], secret_key_hash) - self.assertTrue(credentials['is_active']) - - def test_get_agent_credentials_not_exists(self): - """Test retrieving non-existent agent credentials.""" - credentials = self.db.get_agent_credentials("non_existent_agent") - self.assertIsNone(credentials) - - def test_store_agent_token(self): - """Test storing agent JWT token.""" - agent_id = "agent_test123" - token = "test_jwt_token" - expires_at = (datetime.now() + timedelta(hours=1)).isoformat() - - success = self.db.store_agent_token(agent_id, token, expires_at) - self.assertTrue(success) - - # Verify token exists - stored_token = self.db.get_agent_token(agent_id) - self.assertIsNotNone(stored_token) - self.assertEqual(stored_token['token'], token) - - def test_cleanup_expired_tokens(self): - """Test cleanup of expired tokens.""" - agent_id = "agent_test123" - - # Create expired token - expired_token = "expired_token" - expired_time = (datetime.now() - timedelta(hours=1)).isoformat() - self.db.store_agent_token(agent_id, expired_token, expired_time) - - # Create valid token - valid_token = "valid_token" - valid_time = (datetime.now() + timedelta(hours=1)).isoformat() - self.db.store_agent_token("agent_valid", valid_token, valid_time) - - # Cleanup expired tokens - cleaned = self.db.cleanup_expired_tokens() - self.assertGreaterEqual(cleaned, 1) - - # Verify expired token is gone - token = self.db.get_agent_token(agent_id) - self.assertIsNone(token) - - # Verify valid token remains - token = self.db.get_agent_token("agent_valid") - self.assertIsNotNone(token) - - -class TestIntegration(unittest.TestCase): - """Integration tests for the complete authentication flow.""" - - def setUp(self): - """Set up test fixtures.""" - self.temp_dir = tempfile.mkdtemp() - self.db_path = os.path.join(self.temp_dir, 'test_guardian.db') - self.auth = AgentAuthentication() - - # Use test database - self.original_db_path = self.auth.db_path if hasattr(self.auth, 'db_path') else None - - def tearDown(self): - """Clean up test fixtures.""" - if os.path.exists(self.db_path): - os.remove(self.db_path) - os.rmdir(self.temp_dir) - - def test_complete_authentication_flow(self): - """Test complete agent authentication workflow.""" - # Step 1: Generate agent ID - agent_id = self.auth.generate_agent_id() - self.assertIsNotNone(agent_id) - - # Step 2: Create credentials - credentials = self.auth.create_agent_credentials(agent_id) - self.assertIsNotNone(credentials) - - # Step 3: Generate JWT token - token = self.auth.generate_jwt_token( - credentials['agent_id'], - credentials['secret_key'] - ) - self.assertIsNotNone(token) - - # Step 4: Verify token - is_valid = self.auth.verify_jwt_token(token, credentials['secret_key']) - self.assertTrue(is_valid) - - # Step 5: Create HMAC signature - test_data = "test API request" - signature = self.auth.create_hmac_signature(test_data, credentials['secret_key']) - self.assertIsNotNone(signature) - - # Step 6: Verify HMAC signature - is_signature_valid = self.auth.verify_hmac_signature( - test_data, signature, credentials['secret_key'] - ) - self.assertTrue(is_signature_valid) - - -def run_tests(): - """Run all tests.""" - print("🧪 Running PyGuardian Authentication Tests...") - print("=" * 50) - - # Create test suite - test_suite = unittest.TestSuite() - - # Add test classes - test_classes = [ - TestAgentAuthentication, - TestDatabase, - TestIntegration - ] - - for test_class in test_classes: - tests = unittest.TestLoader().loadTestsFromTestCase(test_class) - test_suite.addTests(tests) - - # Run tests - runner = unittest.TextTestRunner(verbosity=2) - result = runner.run(test_suite) - - # Print summary - print("\n" + "=" * 50) - print(f"🏁 Tests completed:") - print(f" ✅ Passed: {result.testsRun - len(result.failures) - len(result.errors)}") - print(f" ❌ Failed: {len(result.failures)}") - print(f" 💥 Errors: {len(result.errors)}") - - # Return exit code - return 0 if result.wasSuccessful() else 1 - - -if __name__ == '__main__': - sys.exit(run_tests()) \ No newline at end of file diff --git a/install.sh b/install.sh index 5571b1a..6a4a97e 100644 --- a/install.sh +++ b/install.sh @@ -15,7 +15,7 @@ YELLOW='\033[1;33m' NC='\033[0m' # Project information -PYGUARDIAN_VERSION="2.0.0" +PYGUARDIAN_VERSION="2.1.0" PYGUARDIAN_REPO="https://github.com/SmartSolTech/PyGuardian" print_header() { @@ -279,8 +279,16 @@ main() { echo "" echo "Next steps:" echo "1. Configure your Telegram bot token in /opt/pyguardian/config/config.yaml" - echo "2. Start the service: systemctl start pyguardian" - echo "3. Enable auto-start: systemctl enable pyguardian" + echo "2. Review authentication settings in /opt/pyguardian/config/auth.yaml" + echo "3. Start the service: systemctl start pyguardian" + echo "4. Enable auto-start: systemctl enable pyguardian" + echo "" + echo -e "${YELLOW}[INFO]${NC} PyGuardian v2.1.0 includes:" + echo " ✓ JWT-based agent authentication" + echo " ✓ Encrypted agent credentials storage" + echo " ✓ RESTful API server with authentication" + echo " ✓ Comprehensive testing suite" + echo " ✓ CI/CD pipeline with security scanning" echo "" echo "Documentation: ${PYGUARDIAN_REPO}/tree/main/documentation" echo "Support: https://github.com/SmartSolTech/PyGuardian/issues" diff --git a/requirements.txt b/requirements.txt index e3e6255..d213ebf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -53,9 +53,14 @@ psutil>=5.9.0 # Для сигналов (встроенный) # signal - встроенный модуль -# Дополнительные зависимости для разработки и тестирования (опционально) -# pytest>=7.4.0 -# pytest-asyncio>=0.21.0 -# black>=23.9.0 -# flake8>=6.0.0 -# mypy>=1.5.0 \ No newline at end of file +# Дополнительные зависимости для разработки и тестирования +pytest>=7.4.0 +pytest-asyncio>=0.21.0 +pytest-cov>=4.1.0 +black>=23.9.0 +flake8>=6.0.0 +isort>=5.12.0 +bandit>=1.7.5 +safety>=2.3.0 +mkdocs>=1.5.0 +mkdocs-material>=9.4.0 \ No newline at end of file