TL;DR

  • Usa Batfish para análisis de red pre-deployment—valida routing, ACLs y alcanzabilidad antes de tocar cualquier dispositivo
  • Prueba reglas de firewall como código: define reglas allow/deny esperadas, luego verifica con InSpec o assertions personalizadas
  • Implementa tests de topología de red que verifiquen rutas de conectividad, no solo existencia de recursos individuales

Ideal para: Equipos gestionando VPCs cloud, redes multi-cloud o configuraciones on-premise complejas No recomendado si: Tu red es una VPC simple con routing por defecto (usa herramientas integradas del proveedor cloud) Tiempo de lectura: 14 minutos

Los errores de configuración de red causan más outages de lo que la mayoría de equipos cree. Un security group mal configurado, una entrada incorrecta en route table o una regla de firewall faltante puede tumbar producción—y los enfoques de testing tradicionales detectan estos problemas demasiado tarde. Esta guía cubre testing proactivo de red que valida configuraciones antes del deployment.

Para contexto más amplio sobre testing de infraestructura, consulta Testing de Infrastructure as Code y Estrategias de Testing de Terraform.

Enfoques Asistidos por IA

Las herramientas de IA destacan generando tests de validación de red a partir de requisitos y debuggeando problemas complejos de routing.

Generando queries de Batfish desde requisitos:

Necesito validar estos requisitos de red usando Batfish:

1. Subnet de producción (10.0.1.0/24) puede alcanzar subnet de base de datos (10.0.2.0/24) en puerto 5432
2. Ninguna subnet puede alcanzar 0.0.0.0/0 excepto a través del NAT gateway
3. Subnet del load balancer debe ser alcanzable desde internet en puertos 80 y 443
4. Subnet de management (10.0.10.0/24) NO debe ser alcanzable desde ninguna otra subnet

Genera código Python con pybatfish con assertions apropiadas y mensajes de error claros.
Incluye creación de snapshot e inicialización de red.

Debuggeando problemas de routing:

Mi VPC tiene conectividad inesperada. Configuración:

- VPC CIDR: 10.0.0.0/16
- Subnet pública: 10.0.1.0/24 con ruta a IGW
- Subnet privada: 10.0.2.0/24 con ruta a NAT
- Subnet de base de datos: 10.0.3.0/24 (debe estar aislada)

Problema: Subnet de base de datos puede alcanzar internet, pero no debería.
Route tables adjuntas son: [pegar configs de route table]

Usa análisis de traceroute para identificar dónde existe la ruta no deseada.

Creando tests InSpec para reglas de firewall:

Escribe controles InSpec para verificar estas reglas de security group de AWS:

1. SG del tier web permite inbound 443 solo desde SG del ALB
2. SG del tier app permite inbound 8080 solo desde SG del tier web
3. SG del tier DB permite inbound 5432 solo desde SG del tier app
4. Ningún security group permite inbound 22 desde 0.0.0.0/0

Incluye búsqueda de recursos por tag y condiciones skip apropiadas
para recursos faltantes.

Cuándo Usar Diferentes Enfoques de Testing

Framework de Decisión de Estrategia de Testing

Tipo de TestHerramientaCuándo EjecutarQué Detecta
Análisis de configuraciónBatfishAntes de deploymentLoops de routing, hosts inalcanzables, conflictos de ACL
Checks de complianceInSpec/CheckovPipeline CI/CDViolaciones de política, falta de encriptación
Tests de integraciónTerratestDespués de deploymentConectividad real, resolución DNS
Pruebas de conectividadSmoke testsPost-deploymentValidación de flujo de tráfico real
Análisis de impacto de cambiosBatfish diferencialAntes de cambiosEfectos secundarios no intencionales

Usa Batfish Cuando

  • Validación pre-deployment es crítica: Detectar problemas antes de tocar producción
  • Entornos multi-vendor: Analizar configs de Cisco, Juniper, AWS, Azure juntos
  • Routing complejo: Configuraciones BGP, OSPF o VRF necesitan validación
  • Compliance requiere pruebas: Generar reportes de alcanzabilidad para auditores

Usa Terratest/InSpec Cuando

  • Infraestructura cloud-native: VPCs AWS/GCP/Azure con routing simple
  • Verificación de security groups: Confirmar que estado real de firewall coincide con esperado
  • Testing de integración: Verificar que recursos funcionan juntos después del deployment

Batfish para Análisis de Red

Configurando Batfish

# Ejecutar servidor Batfish en Docker
docker run -d -p 9997:9997 -p 9996:9996 batfish/batfish

# Instalar cliente pybatfish
pip install pybatfish

Análisis Básico de Snapshot de Red

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

# Inicializar
load_questions()
bf_set_network("my-network")
bf_init_snapshot("./configs", name="current-config")

# Analizar problemas de configuración
issues = bfq.initIssues().answer().frame()
print(issues[["Type", "Issue_Type", "Details"]])

# Verificar referencias indefinidas
undefined = bfq.undefinedReferences().answer().frame()
assert len(undefined) == 0, f"Se encontraron referencias indefinidas: {undefined}"

Testing de Alcanzabilidad

def test_web_to_database_connectivity():
    """Verificar que tier web puede alcanzar base de datos en puerto 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, "No se encontró ruta de web a base de datos en puerto 5432"

def test_database_internet_isolation():
    """Verificar que base de datos no puede alcanzar internet."""
    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, "Base de datos tiene acceso a internet no deseado"

Análisis Diferencial para Cambios

def test_route_change_impact():
    """Analizar impacto de cambios de route table antes de aplicar."""
    # Cargar configs actuales y propuestas
    bf_init_snapshot("./configs/current", name="current")
    bf_init_snapshot("./configs/proposed", name="proposed")

    # Comparar alcanzabilidad
    diff = bfq.differentialReachability(
        pathConstraints=PathConstraints(
            startLocation="/production.*/",
            endLocation="/database.*/"
        )
    ).answer(
        snapshot="proposed",
        reference_snapshot="current"
    ).frame()

    # Fallar si alguna ruta existente se rompe
    reduced = diff[diff["Snapshot_Action"] == "DENIED"]
    assert len(reduced) == 0, f"Cambio propuesto rompe rutas: {reduced}"

Testing de VPC con Terraform

Testing de Configuración VPC con 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)

    // Verificar VPC creada con CIDR correcto
    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)

    // Verificar configuraciones DNS
    assert.True(t, aws.IsVpcDnsEnabled(t, vpcID, "us-east-1"))
    assert.True(t, aws.IsVpcDnsHostnamesEnabled(t, vpcID, "us-east-1"))
}

Testing de Routing de Subnets

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

    // Después de terraform apply...

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

    // Verificar que subnet pública tiene ruta a 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, "Subnet pública sin ruta IGW")

    // Verificar que subnet privada tiene ruta a 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, "Subnet privada sin ruta NAT")
}

Testing de Security Groups

Controles InSpec para AWS Security Groups

# controls/security_groups.rb

control 'web-tier-sg' do
  impact 1.0
  title 'Security group del tier web solo permite tráfico esperado'

  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 de base de datos solo accesible desde 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 no deben permitir todo 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

Integración en Pipeline CI/CD

Workflow de 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: Instalar pybatfish
        run: pip install pybatfish pytest

      - name: Ejecutar análisis 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: Configurar credenciales AWS
        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: Ejecutar Checkov
        uses: bridgecrewio/checkov-action@v12
        with:
          directory: terraform/networking/
          framework: terraform
          check: CKV_AWS_23,CKV_AWS_24,CKV_AWS_25

Midiendo el Éxito

MétricaAntes del TestingDespués del TestingCómo Rastrear
Outages relacionados con red3-4/mes<1/trimestreReportes de incidentes
Misconfiguraciones de SGEncontradas en auditoríasDetectadas en CIMétricas de pipeline
Rollbacks de cambios20% de cambios<5% de cambiosLogs de deployment
Tiempo medio de detecciónDíasMinutosTimestamps de alertas

Señales de que tu testing de red no funciona:

  • Aún encontrando misconfiguraciones en producción
  • Tests pasan pero conectividad falla después del deployment
  • Batfish y realidad divergen (snapshots desactualizados)
  • Security groups tienen reglas inexplicables

Conclusión

El testing efectivo de configuración de red requiere múltiples capas:

  1. Análisis pre-deployment con Batfish detecta problemas de routing y ACL antes de llegar a dispositivos
  2. Testing de compliance con InSpec/Checkov valida políticas de seguridad
  3. Testing de integración con Terratest confirma conectividad real
  4. Monitoreo continuo detecta drift del estado deseado

La idea clave: prueba la red como sistema, no solo componentes individuales. Un security group puede estar configurado correctamente en aislamiento pero crear problemas en combinación con route tables y NACLs.

Ver También

Recursos Oficiales