TL;DR

  • Используйте Batfish для pre-deployment анализа сети—валидируйте маршрутизацию, ACL и достижимость до касания любого устройства
  • Тестируйте правила firewall как код: определите ожидаемые правила allow/deny, затем проверьте с InSpec или кастомными assertions
  • Реализуйте тесты топологии сети, которые проверяют пути связности, а не только существование отдельных ресурсов

Идеально для: Команд, управляющих облачными VPC, мультиоблачными сетями или сложными on-premise конфигурациями Не подходит если: Ваша сеть — простая VPC с маршрутизацией по умолчанию (используйте встроенные инструменты облачного провайдера) Время чтения: 14 минут

Ошибки сетевой конфигурации вызывают больше сбоев, чем большинство команд осознают. Неправильно настроенная security group, некорректная запись в route table или отсутствующее правило firewall может положить production—и традиционные подходы к тестированию обнаруживают эти проблемы слишком поздно. Это руководство охватывает проактивное тестирование сети, которое валидирует конфигурации до деплоя.

Для более широкого контекста тестирования инфраструктуры, смотрите Тестирование Infrastructure as Code и Стратегии Тестирования Terraform.

Подходы с Использованием ИИ

Инструменты ИИ отлично справляются с генерацией тестов валидации сети из требований и отладкой сложных проблем маршрутизации.

Генерация запросов Batfish из требований:

Мне нужно валидировать эти сетевые требования с помощью Batfish:

1. Production subnet (10.0.1.0/24) может достичь database subnet (10.0.2.0/24) на порту 5432
2. Ни одна subnet не может достичь 0.0.0.0/0 кроме как через NAT gateway
3. Subnet load balancer должна быть достижима из интернета на портах 80 и 443
4. Management subnet (10.0.10.0/24) НЕ должна быть достижима из любой другой subnet

Сгенерируй код Python с pybatfish с правильными assertions и понятными сообщениями об ошибках.
Включи создание snapshot и инициализацию сети.

Отладка проблем маршрутизации:

Моя VPC имеет неожиданную связность. Конфигурация:

- VPC CIDR: 10.0.0.0/16
- Public subnet: 10.0.1.0/24 с маршрутом к IGW
- Private subnet: 10.0.2.0/24 с маршрутом к NAT
- Database subnet: 10.0.3.0/24 (должна быть изолирована)

Проблема: Database subnet может достичь интернет, но не должна.
Присоединённые route tables: [вставить конфигурации route table]

Используй traceroute анализ для определения, где существует нежелательный путь.

Создание InSpec тестов для правил firewall:

Напиши InSpec controls для проверки этих правил AWS security group:

1. SG web tier разрешает inbound 443 только от SG ALB
2. SG app tier разрешает inbound 8080 только от SG web tier
3. SG DB tier разрешает inbound 5432 только от SG app tier
4. Ни одна security group не разрешает inbound 22 от 0.0.0.0/0

Включи поиск ресурсов по тегу и правильные skip conditions
для отсутствующих ресурсов.

Когда Использовать Разные Подходы к Тестированию

Фреймворк Принятия Решений по Стратегии Тестирования

Тип ТестаИнструментКогда ЗапускатьЧто Обнаруживает
Анализ конфигурацииBatfishДо деплояПетли маршрутизации, недоступные хосты, конфликты ACL
Проверки complianceInSpec/CheckovPipeline CI/CDНарушения политик, отсутствие шифрования
Интеграционные тестыTerratestПосле деплояРеальная связность, разрешение DNS
Зонды связностиSmoke testsПосле деплояВалидация реального потока трафика
Анализ влияния измененийBatfish differentialДо измененийНепреднамеренные побочные эффекты

Используйте Batfish Когда

  • Pre-deployment валидация критична: Обнаружить проблемы до касания production
  • Multi-vendor окружения: Анализ конфигов Cisco, Juniper, AWS, Azure вместе
  • Сложная маршрутизация: Конфигурации BGP, OSPF или VRF требуют валидации
  • Compliance требует доказательств: Генерировать отчёты о достижимости для аудиторов

Используйте Terratest/InSpec Когда

  • Cloud-native инфраструктура: AWS/GCP/Azure VPC с простой маршрутизацией
  • Верификация security groups: Подтвердить, что реальное состояние firewall соответствует ожидаемому
  • Интеграционное тестирование: Проверить, что ресурсы работают вместе после деплоя

Batfish для Анализа Сети

Настройка Batfish

# Запуск сервера Batfish в Docker
docker run -d -p 9997:9997 -p 9996:9996 batfish/batfish

# Установка клиента pybatfish
pip install pybatfish

Базовый Анализ Network Snapshot

from pybatfish.client.commands import bf_init_snapshot, bf_set_network
from pybatfish.question import bfq
from pybatfish.question.question import load_questions

# Инициализация
load_questions()
bf_set_network("my-network")
bf_init_snapshot("./configs", name="current-config")

# Анализ проблем конфигурации
issues = bfq.initIssues().answer().frame()
print(issues[["Type", "Issue_Type", "Details"]])

# Проверка неопределённых ссылок
undefined = bfq.undefinedReferences().answer().frame()
assert len(undefined) == 0, f"Найдены неопределённые ссылки: {undefined}"

Тестирование Достижимости

def test_web_to_database_connectivity():
    """Проверить, что web tier может достичь базу данных на порту PostgreSQL."""
    result = bfq.reachability(
        pathConstraints=PathConstraints(
            startLocation="web-server",
            endLocation="database-server"
        ),
        headers=HeaderConstraints(
            dstPorts="5432",
            ipProtocols=["TCP"]
        ),
        actions="SUCCESS"
    ).answer().frame()

    assert len(result) > 0, "Путь от web к базе данных на порту 5432 не найден"

def test_database_internet_isolation():
    """Проверить, что база данных не может достичь интернет."""
    result = bfq.reachability(
        pathConstraints=PathConstraints(
            startLocation="database-server",
            endLocation="internet"
        ),
        headers=HeaderConstraints(
            dstIps="0.0.0.0/0"
        ),
        actions="SUCCESS"
    ).answer().frame()

    assert len(result) == 0, "База данных имеет нежелательный доступ в интернет"

Дифференциальный Анализ для Изменений

def test_route_change_impact():
    """Анализ влияния изменений route table до применения."""
    # Загрузка текущих и предложенных конфигов
    bf_init_snapshot("./configs/current", name="current")
    bf_init_snapshot("./configs/proposed", name="proposed")

    # Сравнение достижимости
    diff = bfq.differentialReachability(
        pathConstraints=PathConstraints(
            startLocation="/production.*/",
            endLocation="/database.*/"
        )
    ).answer(
        snapshot="proposed",
        reference_snapshot="current"
    ).frame()

    # Ошибка если какие-либо существующие пути нарушены
    reduced = diff[diff["Snapshot_Action"] == "DENIED"]
    assert len(reduced) == 0, f"Предложенное изменение нарушает пути: {reduced}"

Тестирование VPC с Terraform

Тестирование Конфигурации VPC с Terratest

package test

import (
    "testing"
    "github.com/gruntwork-io/terratest/modules/aws"
    "github.com/gruntwork-io/terratest/modules/terraform"
    "github.com/stretchr/testify/assert"
)

func TestVPCConfiguration(t *testing.T) {
    t.Parallel()

    terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
        TerraformDir: "../modules/vpc",
        Vars: map[string]interface{}{
            "vpc_cidr":     "10.0.0.0/16",
            "environment":  "test",
            "enable_nat":   true,
        },
    })

    defer terraform.Destroy(t, terraformOptions)
    terraform.InitAndApply(t, terraformOptions)

    // Проверяем VPC создана с правильным CIDR
    vpcID := terraform.Output(t, terraformOptions, "vpc_id")
    vpc := aws.GetVpcById(t, vpcID, "us-east-1")
    assert.Equal(t, "10.0.0.0/16", *vpc.CidrBlock)

    // Проверяем настройки DNS
    assert.True(t, aws.IsVpcDnsEnabled(t, vpcID, "us-east-1"))
    assert.True(t, aws.IsVpcDnsHostnamesEnabled(t, vpcID, "us-east-1"))
}

Тестирование Маршрутизации Subnets

func TestSubnetRouting(t *testing.T) {
    t.Parallel()

    // После terraform apply...

    publicSubnetID := terraform.Output(t, terraformOptions, "public_subnet_id")
    privateSubnetID := terraform.Output(t, terraformOptions, "private_subnet_id")

    // Проверяем, что public subnet имеет маршрут к internet gateway
    publicRoutes := aws.GetRouteTableForSubnet(t, publicSubnetID, "us-east-1")
    hasIGWRoute := false
    for _, route := range publicRoutes.Routes {
        if route.GatewayId != nil && strings.HasPrefix(*route.GatewayId, "igw-") {
            hasIGWRoute = true
            break
        }
    }
    assert.True(t, hasIGWRoute, "Public subnet без маршрута IGW")

    // Проверяем, что private subnet имеет маршрут к NAT gateway
    privateRoutes := aws.GetRouteTableForSubnet(t, privateSubnetID, "us-east-1")
    hasNATRoute := false
    for _, route := range privateRoutes.Routes {
        if route.NatGatewayId != nil {
            hasNATRoute = true
            break
        }
    }
    assert.True(t, hasNATRoute, "Private subnet без маршрута NAT")
}

Тестирование Security Groups

InSpec Controls для AWS Security Groups

# controls/security_groups.rb

control 'web-tier-sg' do
  impact 1.0
  title 'Security group web tier разрешает только ожидаемый трафик'

  web_sg = aws_security_group(group_name: 'web-tier-sg')

  describe web_sg do
    it { should exist }
    it { should allow_in(port: 443, ipv4_range: '0.0.0.0/0') }
    it { should allow_in(port: 80, ipv4_range: '0.0.0.0/0') }
    it { should_not allow_in(port: 22, ipv4_range: '0.0.0.0/0') }
    it { should_not allow_in(port: 3389, ipv4_range: '0.0.0.0/0') }
  end
end

control 'database-tier-sg' do
  impact 1.0
  title 'Tier базы данных доступен только от tier app'

  db_sg = aws_security_group(group_name: 'database-tier-sg')
  app_sg = aws_security_group(group_name: 'app-tier-sg')

  describe db_sg do
    it { should exist }
    it { should allow_in_only(port: 5432, security_group: app_sg.group_id) }
    it { should_not allow_in(ipv4_range: '0.0.0.0/0') }
  end
end

control 'no-wide-open-egress' do
  impact 0.7
  title 'Security groups не должны разрешать весь egress'

  aws_security_groups.group_ids.each do |sg_id|
    describe aws_security_group(group_id: sg_id) do
      it { should_not allow_out(ipv4_range: '0.0.0.0/0', port: '0-65535') }
    end
  end
end

Интеграция в Pipeline CI/CD

Workflow GitHub Actions

name: Network Configuration Tests

on:
  pull_request:
    paths:

      - 'terraform/networking/**'
      - 'configs/network/**'

jobs:
  batfish-analysis:
    runs-on: ubuntu-latest
    services:
      batfish:
        image: batfish/batfish
        ports:

          - 9997:9997
          - 9996:9996

    steps:

      - uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'

      - name: Установка pybatfish
        run: pip install pybatfish pytest

      - name: Запуск анализа Batfish
        run: |
          pytest tests/network/ -v --tb=short
        env:
          BATFISH_HOST: localhost

  terraform-tests:
    runs-on: ubuntu-latest
    needs: batfish-analysis
    steps:

      - uses: actions/checkout@v4

      - name: Setup Terraform
        uses: hashicorp/setup-terraform@v3

      - name: Настройка AWS credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - name: Terraform Validate
        run: terraform validate
        working-directory: terraform/networking/

      - name: Запуск Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: terraform/networking/
          framework: terraform
          check: CKV_AWS_23,CKV_AWS_24,CKV_AWS_25

Измерение Успеха

МетрикаДо ТестированияПосле ТестированияКак Отслеживать
Сбои связанные с сетью3-4/месяц<1/кварталОтчёты об инцидентах
Misconfigurations SGНайдены на аудитахОбнаружены в CIМетрики pipeline
Откаты изменений20% изменений<5% измененийЛоги деплоя
Среднее время обнаруженияДниМинутыTimestamps алертов

Сигналы, что ваше тестирование сети не работает:

  • Всё ещё находите misconfigurations в production
  • Тесты проходят, но связность не работает после деплоя
  • Batfish и реальность расходятся (устаревшие snapshots)
  • Security groups имеют необъяснимые правила

Заключение

Эффективное тестирование сетевой конфигурации требует нескольких слоёв:

  1. Pre-deployment анализ с Batfish обнаруживает проблемы маршрутизации и ACL до достижения устройств
  2. Тестирование compliance с InSpec/Checkov валидирует политики безопасности
  3. Интеграционное тестирование с Terratest подтверждает реальную связность
  4. Непрерывный мониторинг обнаруживает drift от желаемого состояния

Ключевая идея: тестируйте сеть как систему, а не только отдельные компоненты. Security group может быть настроена правильно в изоляции, но создавать проблемы в комбинации с route tables и NACLs.

Смотрите также

Официальные ресурсы