Imagen destacada del artículo: VPC avanzada: peering, transit gateway y seguridad en entornos reales

VPC avanzada: peering, transit gateway y seguridad en entornos reales

Cuando un proyecto crece, la VPC deja de ser “la red” y pasa a ser una pieza dentro de un sistema de redes.

Aparecen:

  • 🏢 Múltiples cuentas AWS
  • 🔄 Varios entornos (dev, staging, prod)
  • 👥 Equipos distintos con necesidades diferentes
  • 🔒 Requisitos de seguridad más estrictos
  • 🎯 Necesidad de aislar sin romper la comunicación

Aquí es donde muchas arquitecturas se vuelven frágiles. No porque AWS no ofrezca soluciones, sino porque se intenta escalar un diseño que solo servía para una VPC pequeña.

He visto arquitecturas con 40+ VPC Peerings en malla, VPCs con 200+ reglas en Security Groups y equipos que tardaban semanas en entender qué hablaba con qué.

Este artículo va de ese punto de inflexión: cuando dejas de pensar en “una VPC” y empiezas a diseñar arquitecturas de red en AWS.

El problema: una VPC ya no es suficiente

En cuanto separas:

Necesidades de separación:
  
  Por entorno:
    - VPC Development
    - VPC Staging
    - VPC Production
    - VPC DR (Disaster Recovery)
  
  Por equipo/dominio:
    - VPC Platform Team
    - VPC Data Team
    - VPC ML Team
    - VPC Security Team
  
  Por nivel de seguridad:
    - VPC Public Services
    - VPC Internal Services
    - VPC Sensitive Data (PCI, PII)
    - VPC Compliance Zone
  
  Por función:
    - VPC Shared Services
    - VPC Workloads
    - VPC Egress (salida a Internet)
    - VPC Inspection (tráfico filtrado)

Una sola VPC se convierte en un problema de:

  • Blast radius: un error afecta a todo
  • Límites de servicio: 200 route tables, 500 Security Groups por VPC
  • Complejidad: reglas imposibles de auditar
  • Falta de aislamiento: equipos pisándose entre sí

La solución correcta no es meter más subredes ni más reglas, sino separar VPCs y conectarlas de forma explícita y controlada.

VPC Peering: simple, directo… y limitado

El VPC Peering permite conectar dos VPCs de forma privada, sin pasar por Internet. Es rápido, sencillo y funciona bien cuando el número de VPCs es pequeño.

Características clave:

VPC Peering:
  Protocolo: AWS PrivateLink (bajo el capó)
  Latencia: Baja (red interna AWS)
  Coste: $0.01/GB transferido
  Límites: 125 peerings activos por VPC
  DNS: Soportado (con configuración)
  Seguridad: Security Groups pueden referenciar peering

Configuración básica:

# Terraform: VPC Peering entre Production y Shared Services
resource "aws_vpc_peering_connection" "prod_to_shared" {
  vpc_id        = aws_vpc.production.id
  peer_vpc_id   = aws_vpc.shared_services.id
  auto_accept   = true

  tags = {
    Name = "prod-to-shared-peering"
    Side = "Requester"
  }
}

# Rutas en VPC Production
resource "aws_route" "prod_to_shared" {
  route_table_id            = aws_route_table.production_private.id
  destination_cidr_block    = aws_vpc.shared_services.cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.prod_to_shared.id
}

# Rutas en VPC Shared Services
resource "aws_route" "shared_to_prod" {
  route_table_id            = aws_route_table.shared_private.id
  destination_cidr_block    = aws_vpc.production.cidr_block
  vpc_peering_connection_id = aws_vpc_peering_connection.prod_to_shared.id
}

# Security Group permitiendo tráfico desde peering
resource "aws_security_group_rule" "allow_from_prod_vpc" {
  type              = "ingress"
  from_port         = 443
  to_port           = 443
  protocol          = "tcp"
  cidr_blocks       = [aws_vpc.production.cidr_block]
  security_group_id = aws_security_group.shared_services.id
  description       = "HTTPS from Production VPC via peering"
}

La limitación crítica: no es transitivo

Escenario problemático:
  
  VPC A (10.0.0.0/16) <--peering--> VPC B (10.1.0.0/16)
  VPC B (10.1.0.0/16) <--peering--> VPC C (10.2.0.0/16)
  
  Resultado:
    ✅ A puede hablar con B
    ✅ B puede hablar con C
    ❌ A NO puede hablar con C directamente
  
  Solución pobre:
    - Crear peering A <-> C
    - Resultado: explosión combinatoria de peerings
  
  Solución correcta:
    - Transit Gateway (siguiente sección)

Cuándo usar VPC Peering:

Casos buenos:
  ✅ Conexión entre 2-5 VPCs
  ✅ Topología simple y estable
  ✅ No hay requisitos de transitividad
  ✅ Bajo coste es prioridad
  ✅ Arquitectura pequeña/mediana
  
  Ejemplos:
    - Prod <-> Shared Services
    - Dev <-> Testing
    - App VPC <-> Data VPC

Casos malos:
  ❌ Más de 10 VPCs
  ❌ Topología hub-and-spoke
  ❌ Necesidad de centralizar routing
  ❌ Integración con VPN/Direct Connect
  ❌ Arquitectura en crecimiento

Límite mental:
  "Si necesitas dibujar la topología para entenderla,
   probablemente necesitas Transit Gateway"

Explosión combinatoria (el problema real):

Número de VPCs vs Peerings necesarios (malla completa):

  2 VPCs  → 1 peering
  3 VPCs  → 3 peerings
  5 VPCs  → 10 peerings
  10 VPCs → 45 peerings
  20 VPCs → 190 peerings
  
Fórmula: n*(n-1)/2

Resultado con 20 VPCs:
  - 190 peering connections
  - 190 * 2 = 380 route entries
  - Inauditable, imposible de mantener
  - Cambiar algo = pesadilla operacional

Por eso el peering funciona bien para casos simples. Cuando empiezas a crecer de verdad, se queda corto.

Transit Gateway: pensar la red como sistema

Aquí entra el AWS Transit Gateway, uno de los recursos más infravalorados y más potentes de AWS. No es barato, pero ordena el caos.

Arquitectura hub-and-spoke:

Transit Gateway (hub central):

  ├── VPC Production
  ├── VPC Development
  ├── VPC Shared Services
  ├── VPC Data Platform
  ├── VPN Site-to-Site (oficina)
  ├── Direct Connect (datacenter)
  └── VPC Peering (otra región)

Ventajas:
  ✅ Una única conexión por VPC
  ✅ Routing centralizado
  ✅ Tráfico filtrado en un punto
  ✅ Escalabilidad lineal (no exponencial)
  ✅ Integración híbrida nativa

Configuración Transit Gateway:

# Terraform: Transit Gateway completo
resource "aws_ec2_transit_gateway" "main" {
  description                     = "Main Transit Gateway for Organization"
  default_route_table_association = "disable"
  default_route_table_propagation = "disable"
  dns_support                     = "enable"
  vpn_ecmp_support               = "enable"
  
  tags = {
    Name        = "org-transit-gateway"
    Environment = "shared"
    ManagedBy   = "terraform"
  }
}

# Route Tables segmentadas
resource "aws_ec2_transit_gateway_route_table" "production" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id
  
  tags = {
    Name = "production-route-table"
  }
}

resource "aws_ec2_transit_gateway_route_table" "development" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id
  
  tags = {
    Name = "development-route-table"
  }
}

resource "aws_ec2_transit_gateway_route_table" "shared_services" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id
  
  tags = {
    Name = "shared-services-route-table"
  }
}

# Attachment de VPC Production
resource "aws_ec2_transit_gateway_vpc_attachment" "production" {
  subnet_ids         = aws_subnet.production_tgw[*].id
  transit_gateway_id = aws_ec2_transit_gateway.main.id
  vpc_id             = aws_vpc.production.id
  
  dns_support                                     = "enable"
  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false
  
  tags = {
    Name = "production-vpc-attachment"
  }
}

# Asociación a route table específica
resource "aws_ec2_transit_gateway_route_table_association" "production" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.production.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.production.id
}

# Propagación selectiva (Prod puede hablar con Shared, no con Dev)
resource "aws_ec2_transit_gateway_route_table_propagation" "prod_to_shared" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.shared_services.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.production.id
}

# Rutas estáticas para control fino
resource "aws_ec2_transit_gateway_route" "prod_default_to_inspection" {
  destination_cidr_block         = "0.0.0.0/0"
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.inspection.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.production.id
}

# En VPC: ruta hacia Transit Gateway
resource "aws_route" "production_to_tgw" {
  route_table_id         = aws_route_table.production_private.id
  destination_cidr_block = "10.0.0.0/8"  # Rango corporativo completo
  transit_gateway_id     = aws_ec2_transit_gateway.main.id
}

Segmentación con Route Tables:

Estrategia de aislamiento:

Route Table "Production":
  Asociado a: VPC Production
  Propaga hacia: Shared Services, Inspection VPC
  NO propaga hacia: Development, Testing
  
Route Table "Development":
  Asociado a: VPC Development, VPC Testing
  Propaga hacia: Shared Services
  NO propaga hacia: Production
  
Route Table "Shared Services":
  Asociado a: VPC Shared Services
  Propaga hacia: TODAS las VPCs (hub central)
  
Route Table "Egress":
  Asociado a: VPC Egress (NAT Gateway centralizado)
  Ruta default: Internet Gateway
  Resto: deny implícito

Resultado:
  ✅ Production aislado de Development
  ✅ Todo pasa por Shared Services
  ✅ Tráfico a Internet centralizado
  ✅ Auditoría simplificada

Patrón de inspección centralizada:

Arquitectura con Inspection VPC:

  Internet

  [Inspection VPC]
    - Firewall (AWS Network Firewall)
    - IDS/IPS
    - Traffic logging

  Transit Gateway

  Workload VPCs

Ventajas:
  ✅ Un solo punto de inspección
  ✅ Reglas centralizadas
  ✅ Costes optimizados
  ✅ Visibilidad completa
  ✅ Cumplimiento simplificado

Costes Transit Gateway:

Pricing (us-east-1):
  
  Attachment: $0.05/hora = ~$36/mes por VPC
  Data Transfer: $0.02/GB
  
Ejemplo con 10 VPCs:
  - 10 attachments × $36 = $360/mes
  - 100 TB transfer × $0.02/GB = $2,000/mes
  - Total: ~$2,360/mes
  
vs VPC Peering (10 VPCs = 45 peerings):
  - 0 coste fijo
  - 100 TB × $0.01/GB = $1,000/mes
  
Transit Gateway es más caro, pero:
  ✅ Operacionalmente más simple
  ✅ Escalable sin límite práctico
  ✅ Integración híbrida incluida
  ✅ Routing centralizado
  
Decisión: a partir de 5-7 VPCs, TGW compensa.

Cuando la red deja de ser algo táctico y pasa a ser estratégico, Transit Gateway es la solución correcta.

VPC Endpoints: eliminar Internet de la ecuación

Uno de los mayores saltos de madurez en AWS es dejar de depender de Internet para hablar con servicios gestionados.

El problema con NAT Gateway:

Arquitectura inmadura:
  
  EC2 en subnet privada

  NAT Gateway (subnet pública)

  Internet Gateway

  Internet

  AWS S3 endpoint público
  
Problemas:
  ❌ Latencia adicional
  ❌ Coste NAT: $0.045/hora + $0.045/GB
  ❌ Superficie de ataque aumentada
  ❌ Dependencia de Internet
  ❌ Tráfico auditable solo con Flow Logs

La solución: VPC Endpoints

Tipos de VPC Endpoints:

1. Gateway Endpoints (gratis):
   - S3
   - DynamoDB
   - Routing via route tables
   
2. Interface Endpoints (PrivateLink):
   - +100 servicios AWS
   - SaaS de terceros
   - Tus propios servicios
   - Basados en ENI (IP privada)
   - Coste: $0.01/hora + $0.01/GB

Configuración Gateway Endpoint (S3):

# Terraform: VPC Endpoint para S3
resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.production.id
  service_name = "com.amazonaws.us-east-1.s3"
  
  tags = {
    Name = "s3-gateway-endpoint"
  }
}

# Asociar a route tables
resource "aws_vpc_endpoint_route_table_association" "s3_private" {
  route_table_id  = aws_route_table.production_private.id
  vpc_endpoint_id = aws_vpc_endpoint.s3.id
}

# Bucket policy: solo acceso desde VPC
resource "aws_s3_bucket_policy" "private_access" {
  bucket = aws_s3_bucket.data.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Sid    = "DenyAccessFromInternet"
        Effect = "Deny"
        Principal = "*"
        Action = "s3:*"
        Resource = [
          "${aws_s3_bucket.data.arn}",
          "${aws_s3_bucket.data.arn}/*"
        ]
        Condition = {
          StringNotEquals = {
            "aws:SourceVpce" = aws_vpc_endpoint.s3.id
          }
        }
      }
    ]
  })
}

Configuración Interface Endpoint (Secrets Manager):

# Interface Endpoint con Security Group
resource "aws_vpc_endpoint" "secrets_manager" {
  vpc_id              = aws_vpc.production.id
  service_name        = "com.amazonaws.us-east-1.secretsmanager"
  vpc_endpoint_type   = "Interface"
  
  subnet_ids          = aws_subnet.production_private[*].id
  security_group_ids  = [aws_security_group.vpc_endpoints.id]
  
  private_dns_enabled = true  # Importante: DNS interno
  
  tags = {
    Name = "secretsmanager-interface-endpoint"
  }
}

# Security Group para endpoints
resource "aws_security_group" "vpc_endpoints" {
  name        = "vpc-endpoints-sg"
  description = "Security group for VPC Interface Endpoints"
  vpc_id      = aws_vpc.production.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [aws_vpc.production.cidr_block]
    description = "HTTPS from VPC"
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

Estrategia completa de endpoints:

VPC Endpoints recomendados:

Gateway Endpoints (GRATIS):
  ✅ S3 (siempre)
  ✅ DynamoDB (si lo usas)

Interface Endpoints (coste/beneficio):
  ✅ Secrets Manager (secretos sensibles)
  ✅ Systems Manager (gestión de instancias)
  ✅ ECR (Docker images privadas)
  ✅ CloudWatch Logs (logging sin Internet)
  ✅ STS (AssumeRole sin salir de VPC)
  ✅ KMS (cifrado crítico)
  
Opcionales (según caso):
  - Lambda (invocaciones privadas)
  - SNS/SQS (mensajería privada)
  - API Gateway (APIs privadas)
  - ECS/EKS (orchestration)
  - RDS Data API
  - Athena
  - Glue

Coste ejemplo (10 endpoints):
  - 10 × $0.01/hora = $0.10/hora
  - $0.10 × 730 horas = $73/mes
  - Transfer: variable según uso
  
Ahorro NAT Gateway:
  - 1 NAT = $33/mes + transfer
  - 3 AZs = $99/mes + transfer
  
Decisión: endpoints casi siempre compensan

Ventajas reales:

Beneficios VPC Endpoints:

Seguridad:
  ✅ Tráfico nunca sale de AWS
  ✅ No atraviesa Internet
  ✅ No expuesto a ataques MITM
  ✅ Compliance simplificado

Performance:
  ✅ Latencia reducida (red interna)
  ✅ Ancho de banda mayor
  ✅ No limitado por NAT Gateway

Coste:
  ✅ Elimina o reduce NAT Gateway
  ✅ Transfer más barato
  ✅ Escalabilidad sin coste adicional

Operacional:
  ✅ Menos dependencias
  ✅ Menos puntos de fallo
  ✅ Más simple de auditar
  ✅ Requisito para redes 100% privadas

Si tu VPC necesita Internet para hablar con S3, todavía estás en una fase inmadura de diseño.

Cuando varios equipos, cuentas o incluso empresas necesitan consumir servicios internos, AWS PrivateLink es la solución elegante.

Compartir servicio entre cuentas/VPCs:

Opción 1: VPC Peering completo
  ❌ Expone toda la VPC
  ❌ Routing complejo
  ❌ Security Groups complejos
  
Opción 2: Exponer a Internet + restricción IP
  ❌ Superficie de ataque aumentada
  ❌ Dependencia de IPs públicas
  ❌ Problemas de compliance

Opción 3: VPN/Direct Connect
  ❌ Coste alto
  ❌ Complejidad operacional
  ❌ Latencia variable
AWS PrivateLink:
  
  Provider (quien expone):
    - Crea Network Load Balancer
    - Crea VPC Endpoint Service
    - Aprueba conexiones (whitelist)
  
  Consumer (quien consume):
    - Crea Interface Endpoint
    - Conecta al servicio via DNS privado
    - Sin routing complejo
  
  Características:
    ✅ Tráfico privado (nunca Internet)
    ✅ Aislamiento total entre VPCs
    ✅ Escalabilidad automática
    ✅ Funciona cross-account
    ✅ Funciona cross-region
# Terraform: Exponer servicio via PrivateLink

# NLB para el servicio
resource "aws_lb" "service" {
  name               = "internal-api-nlb"
  internal           = true
  load_balancer_type = "network"
  subnets            = aws_subnet.provider_private[*].id
  
  tags = {
    Name = "internal-api-nlb"
  }
}

resource "aws_lb_target_group" "service" {
  name     = "internal-api-tg"
  port     = 443
  protocol = "TCP"
  vpc_id   = aws_vpc.provider.id
  
  health_check {
    enabled  = true
    protocol = "TCP"
    port     = 443
  }
}

resource "aws_lb_listener" "service" {
  load_balancer_arn = aws_lb.service.arn
  port              = 443
  protocol          = "TCP"
  
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.service.arn
  }
}

# VPC Endpoint Service
resource "aws_vpc_endpoint_service" "internal_api" {
  acceptance_required        = true  # Aprobar conexiones manualmente
  network_load_balancer_arns = [aws_lb.service.arn]
  
  allowed_principals = [
    "arn:aws:iam::123456789012:root",  # Cuenta consumer
    "arn:aws:iam::987654321098:root"   # Otra cuenta
  ]
  
  tags = {
    Name = "internal-api-endpoint-service"
  }
}

# Output para compartir con consumers
output "vpc_endpoint_service_name" {
  value = aws_vpc_endpoint_service.internal_api.service_name
  # Ejemplo: com.amazonaws.vpce.us-east-1.vpce-svc-1234567890abcdef0
}
# Terraform: Consumir servicio via PrivateLink

# Interface Endpoint al servicio
resource "aws_vpc_endpoint" "internal_api" {
  vpc_id              = aws_vpc.consumer.id
  service_name        = "com.amazonaws.vpce.us-east-1.vpce-svc-1234567890abcdef0"
  vpc_endpoint_type   = "Interface"
  
  subnet_ids         = aws_subnet.consumer_private[*].id
  security_group_ids = [aws_security_group.privatelink_consumer.id]
  
  private_dns_enabled = false  # Usar DNS manual o Route53 Private Hosted Zone
  
  tags = {
    Name = "internal-api-consumer-endpoint"
  }
}

# Security Group consumer
resource "aws_security_group" "privatelink_consumer" {
  name        = "privatelink-consumer-sg"
  vpc_id      = aws_vpc.consumer.id

  egress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
    description = "HTTPS to PrivateLink endpoint"
  }
}

# DNS privado (opcional pero recomendado)
resource "aws_route53_zone" "private" {
  name = "internal-api.company.local"
  
  vpc {
    vpc_id = aws_vpc.consumer.id
  }
}

resource "aws_route53_record" "api" {
  zone_id = aws_route53_zone.private.zone_id
  name    = "api.internal-api.company.local"
  type    = "A"
  
  alias {
    name                   = aws_vpc_endpoint.internal_api.dns_entry[0].dns_name
    zone_id                = aws_vpc_endpoint.internal_api.dns_entry[0].hosted_zone_id
    evaluate_target_health = true
  }
}
Escenarios perfectos:

1. Plataforma interna multi-tenant:
   - Equipo Platform expone APIs
   - Equipos de producto consumen
   - Cada equipo en su propia VPC/cuenta
   - Aislamiento total, consumo controlado

2. Servicios compartidos:
   - Shared Services VPC (LDAP, DNS, monitoring)
   - Todas las VPCs consumen via PrivateLink
   - No hay peerings ni routing complejo

3. SaaS B2B:
   - Empresa A expone API a empresa B
   - Sin exponer a Internet
   - Tráfico privado AWS-to-AWS

4. Multi-región:
   - Servicio en us-east-1
   - Consumers en eu-west-1
   - PrivateLink inter-region

5. Compliance estricto:
   - PCI-DSS, HIPAA, ISO27001
   - Requisito: datos nunca en Internet
   - PrivateLink = diseño conforme

Es especialmente potente en organizaciones grandes o plataformas internas. Más complejo de configurar, sí, pero extremadamente limpio.

Integración híbrida: cuando la VPC se conecta al mundo real

En muchos entornos reales, AWS no vive aislado. Hay:

Escenarios híbridos comunes:

Oficinas corporativas:
  - Usuarios necesitan acceder a workloads AWS
  - Solución: Client VPN, Direct Connect

Datacenters legacy:
  - Aplicaciones on-premise + AWS
  - Solución: Site-to-Site VPN, Direct Connect

Otros cloud providers:
  - Multi-cloud (AWS + Azure/GCP)
  - Solución: VPN IPsec, SD-WAN

Partners/Proveedores:
  - Integración B2B privada
  - Solución: PrivateLink, Direct Connect Gateway

Site-to-Site VPN:

# Terraform: VPN Site-to-Site
resource "aws_customer_gateway" "office" {
  bgp_asn    = 65000
  ip_address = "203.0.113.45"  # IP pública de la oficina
  type       = "ipsec.1"
  
  tags = {
    Name = "office-customer-gateway"
  }
}

resource "aws_vpn_gateway" "main" {
  vpc_id = aws_vpc.production.id
  
  tags = {
    Name = "production-vpn-gateway"
  }
}

resource "aws_vpn_connection" "office" {
  vpn_gateway_id      = aws_vpn_gateway.main.id
  customer_gateway_id = aws_customer_gateway.office.id
  type                = "ipsec.1"
  static_routes_only  = false  # BGP dinámico
  
  tunnel1_inside_cidr   = "169.254.10.0/30"
  tunnel2_inside_cidr   = "169.254.10.4/30"
  tunnel1_preshared_key = var.vpn_preshared_key_1
  tunnel2_preshared_key = var.vpn_preshared_key_2
  
  tags = {
    Name = "office-vpn-connection"
  }
}

# Habilitar route propagation
resource "aws_vpn_gateway_route_propagation" "private" {
  vpn_gateway_id = aws_vpn_gateway.main.id
  route_table_id = aws_route_table.production_private.id
}

Client VPN (acceso usuarios):

# AWS Client VPN para usuarios remotos
resource "aws_ec2_client_vpn_endpoint" "users" {
  description            = "Client VPN for remote users"
  server_certificate_arn = aws_acm_certificate.vpn_server.arn
  client_cidr_block      = "172.16.0.0/22"  # Pool para clientes
  
  authentication_options {
    type                       = "certificate-authentication"
    root_certificate_chain_arn = aws_acm_certificate.vpn_client_root.arn
  }
  
  connection_log_options {
    enabled              = true
    cloudwatch_log_group = aws_cloudwatch_log_group.vpn.name
  }
  
  dns_servers = [
    cidrhost(aws_vpc.production.cidr_block, 2)  # AWS DNS
  ]
  
  split_tunnel = true  # Solo tráfico AWS por VPN
  
  tags = {
    Name = "user-client-vpn"
  }
}

resource "aws_ec2_client_vpn_network_association" "users" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.users.id
  subnet_id              = aws_subnet.production_private[0].id
}

resource "aws_ec2_client_vpn_authorization_rule" "users" {
  client_vpn_endpoint_id = aws_ec2_client_vpn_endpoint.users.id
  target_network_cidr    = aws_vpc.production.cidr_block
  authorize_all_groups   = true
  description            = "Allow access to VPC"
}

Direct Connect (conexión dedicada):

AWS Direct Connect:

Características:
  - Conexión dedicada 1/10/100 Gbps
  - Latencia consistente (< 5ms típico)
  - No usa Internet
  - Coste fijo mensual
  
Precios (us-east-1):
  - 1 Gbps: ~$0.30/hora = $219/mes + transfer
  - 10 Gbps: ~$2.25/hora = $1,642/mes + transfer
  - Transfer: $0.02/GB (más barato que Internet)
  
Casos de uso:
  ✅ Datacenters con tráfico constante
  ✅ Migraciones masivas de datos
  ✅ Latencia crítica
  ✅ Requisitos de ancho de banda alto
  ✅ Compliance (datos no en Internet)
  
Tiempo de provisión: 30-90 días
Vendor necesario: sí (Equinix, Megaport, etc.)

El mayor error: solapamiento de CIDRs

Problema real:

Red corporativa:
  Oficina HQ:    10.0.0.0/16
  Datacenter:    10.1.0.0/16
  Branch Office: 10.2.0.0/16

AWS VPCs (mal diseñadas):
  VPC Production: 10.0.0.0/16  ❌ SOLAPA CON HQ
  VPC Development: 10.1.0.0/16  ❌ SOLAPA CON DC

Consecuencias:
  ❌ Routing imposible
  ❌ NAT necesario (pérdida de visibilidad)
  ❌ Complejidad operacional extrema
  ❌ "Soluciones" poco limpias

Solución correcta (diseño previo):
  
  Rango corporativo global: 10.0.0.0/8
  
  Asignación:
    - On-premise:     10.0.0.0/12   (10.0-10.15)
    - AWS us-east-1:  10.16.0.0/12  (10.16-10.31)
    - AWS eu-west-1:  10.32.0.0/12  (10.32-10.47)
    - Futuro:         10.48.0.0/12+ (reservado)
  
  Dentro de AWS us-east-1:
    - Production:     10.16.0.0/16
    - Development:    10.17.0.0/16
    - Shared:         10.18.0.0/16
    - Data:           10.19.0.0/16
    - ...

Regla de oro:
  "Planifica CIDRs ANTES de crear la primera VPC.
   Cambiarlos después = migración dolorosa."

Diseñar híbrido exige orden mental y paciencia. Pero cuando está bien hecho, la VPC se convierte en una extensión natural de la red corporativa.

Seguridad en serio: aislar por diseño

En VPC avanzadas, la seguridad ya no se basa solo en Security Groups. Se basa en arquitectura defensiva.

Principios de seguridad:

Defense in Depth para VPCs:

1. Separación por cuentas:
   ✅ AWS Organizations con OUs
   ✅ Prod en cuenta separada
   ✅ SCPs para límites
   
2. Aislamiento por VPC:
   ✅ Workloads diferentes = VPCs diferentes
   ✅ Blast radius contenido
   ✅ Políticas diferentes
   
3. Segmentación por subredes:
   ✅ Public, Private, Data tiers
   ✅ NACLs como defensa adicional
   ✅ Routing explícito
   
4. Security Groups como firewall:
   ✅ Mínimo privilegio
   ✅ Nombrados y documentados
   ✅ Sin reglas 0.0.0.0/0 salvo justificación
   
5. Endpoints privados:
   ✅ Sin dependencia de Internet
   ✅ Gateway Endpoints (S3, DynamoDB)
   ✅ Interface Endpoints (otros servicios)
   
6. Tráfico inspeccionado:
   ✅ AWS Network Firewall
   ✅ Inspection VPC centralizada
   ✅ IDS/IPS si compliance lo exige
   
7. Logging completo:
   ✅ VPC Flow Logs (ALL tráfico)
   ✅ CloudTrail (cambios de config)
   ✅ GuardDuty (threat detection)
   
8. Exposición mínima:
   ✅ Lo que no necesita Internet, no lo tiene
   ✅ Bastion hosts solo si imprescindible
   ✅ Systems Manager Session Manager mejor

Arquitectura de referencia segura:

VPC Production (10.16.0.0/16):
  
  Public Subnets (10.16.0.0/20):
    - ALB/NLB únicamente
    - NAT Gateways (si necesario)
    - Bastion (deprecated, usar SSM)
    - Security Groups MUY restrictivos
  
  Private Subnets - App Tier (10.16.16.0/20):
    - EC2, ECS, EKS workloads
    - Sin Internet direct
    - Acceso vía endpoints
    - Security Groups por función
  
  Private Subnets - Data Tier (10.16.32.0/20):
    - RDS, ElastiCache, DocumentDB
    - Sin Internet NUNCA
    - Acceso solo desde App Tier
    - Encryption at rest obligatorio
  
  Transit Gateway Subnets (10.16.255.0/28):
    - Subnets dedicadas para TGW attachments
    - Una por AZ
    - Sin instancias
  
  VPC Endpoints (Interface):
    - En subnets privadas
    - Security Group dedicado
    - DNS privado habilitado
  
  Routing:
    - Default route: Transit Gateway (inspección)
    - Rutas específicas: VPC Endpoints
    - Internet: solo public subnets
  
  NACLs:
    - Capa adicional de defensa
    - Deny explícito para rangos sospechosos
    - Allow tráfico legítimo
  
  Monitoring:
    - Flow Logs: CloudWatch + S3
    - GuardDuty: activo
    - Config Rules: compliance continuo
    - Security Hub: dashboard central

Network Firewall para inspección:

# Terraform: AWS Network Firewall
resource "aws_networkfirewall_firewall_policy" "inspection" {
  name = "inspection-firewall-policy"

  firewall_policy {
    stateless_default_actions          = ["aws:forward_to_sfe"]
    stateless_fragment_default_actions = ["aws:forward_to_sfe"]
    
    stateful_rule_group_reference {
      resource_arn = aws_networkfirewall_rule_group.block_malware.arn
    }
    
    stateful_rule_group_reference {
      resource_arn = aws_networkfirewall_rule_group.allow_domains.arn
    }
  }
}

resource "aws_networkfirewall_rule_group" "block_malware" {
  capacity = 100
  name     = "block-malware-domains"
  type     = "STATEFUL"
  
  rule_group {
    rules_source {
      rules_source_list {
        generated_rules_type = "DENYLIST"
        target_types         = ["HTTP_HOST", "TLS_SNI"]
        targets              = [
          ".malware.com",
          ".phishing.net"
        ]
      }
    }
  }
}

resource "aws_networkfirewall_firewall" "inspection" {
  name                = "inspection-firewall"
  firewall_policy_arn = aws_networkfirewall_firewall_policy.inspection.arn
  vpc_id              = aws_vpc.inspection.id
  
  subnet_mapping {
    subnet_id = aws_subnet.inspection_firewall[0].id
  }
  
  subnet_mapping {
    subnet_id = aws_subnet.inspection_firewall[1].id
  }
  
  subnet_mapping {
    subnet_id = aws_subnet.inspection_firewall[2].id
  }
}

VPC Flow Logs para visibilidad:

# Flow Logs a CloudWatch
resource "aws_flow_log" "production_cloudwatch" {
  log_destination_type = "cloud-watch-logs"
  log_destination      = aws_cloudwatch_log_group.vpc_flow_logs.arn
  traffic_type         = "ALL"
  vpc_id               = aws_vpc.production.id
  iam_role_arn         = aws_iam_role.flow_logs.arn
  
  tags = {
    Name = "production-vpc-flow-logs"
  }
}

# Flow Logs a S3 (largo plazo, análisis)
resource "aws_flow_log" "production_s3" {
  log_destination_type = "s3"
  log_destination      = aws_s3_bucket.vpc_flow_logs.arn
  traffic_type         = "ALL"
  vpc_id               = aws_vpc.production.id
  
  destination_options {
    file_format        = "parquet"  # Más eficiente para Athena
    per_hour_partition = true
  }
}

# Análisis con Athena
resource "aws_athena_named_query" "top_talkers" {
  name     = "vpc-flow-logs-top-talkers"
  database = "vpc_flow_logs_db"
  
  query = <<-SQL
    SELECT 
      srcaddr,
      dstaddr,
      SUM(bytes) as total_bytes,
      COUNT(*) as connection_count
    FROM vpc_flow_logs
    WHERE action = 'ACCEPT'
    GROUP BY srcaddr, dstaddr
    ORDER BY total_bytes DESC
    LIMIT 100;
  SQL
}

El principio es claro: lo que no necesita comunicarse, no debería poder hacerlo.

Esto reduce riesgos, simplifica auditorías y hace que los incidentes sean más fáciles de contener.

Arquitecturas de referencia

Patrón 1: Multi-Account con Transit Gateway

Organización AWS:
  ├── Management Account
  ├── Network Account (Transit Gateway hub)
  ├── Shared Services Account
  ├── Production Account
  ├── Development Account
  └── Security Account (Inspection VPC)

Network Account:
  - Transit Gateway (hub central)
  - Route Tables por entorno
  - VPN Gateway
  - Direct Connect Gateway
  - RAM sharing a otras cuentas

Shared Services Account:
  - Active Directory
  - DNS resolvers
  - Monitoring tools
  - CI/CD platform

Production Account:
  - VPC Production
  - Attached a TGW
  - VPC Endpoints
  - Private subnets only

Security Account:
  - Inspection VPC
  - Network Firewall
  - IDS/IPS
  - Logging centralizado

Patrón 2: Egress VPC centralizado

Arquitectura:
  
  Workload VPCs (sin Internet Gateway):

  Transit Gateway

  Egress VPC:
    - NAT Gateways (3 AZs)
    - Proxy servers (opcional)
    - Network Firewall
    - Internet Gateway

  Internet

Ventajas:
  ✅ Un solo punto de salida a Internet
  ✅ Costes NAT optimizados
  ✅ Inspección centralizada
  ✅ Políticas uniformes
  ✅ Auditoría simplificada
  ✅ Workload VPCs 100% privadas

Patrón 3: Multi-región con replicación

us-east-1 (Primary):
  Transit Gateway US
    ├── VPC Production US
    ├── VPC Shared Services US
    └── VPC Data US
  
eu-west-1 (DR/Secondary):
  Transit Gateway EU
    ├── VPC Production EU
    ├── VPC Shared Services EU
    └── VPC Data EU

Interconexión:
  - Transit Gateway Peering (cross-region)
  - Route53 failover routing
  - Data replication (RDS, S3)
  - Active-Active o Active-Standby

Conclusión: cuando la red deja de ser invisible

Al principio, la VPC parece un requisito más. En entornos maduros, se convierte en una pieza clave de la arquitectura.

La evolución:

Fase 1 - Básico:
  - Una VPC
  - Subnets públicas/privadas
  - NAT Gateway
  - "Funciona"

Fase 2 - Crecimiento:
  - Múltiples VPCs
  - VPC Peering
  - Más Security Groups
  - Complejidad creciente

Fase 3 - Madurez:
  - Transit Gateway
  - VPC Endpoints
  - PrivateLink
  - Arquitectura pensada

Fase 4 - Excelencia:
  - Multi-cuenta
  - Seguridad por diseño
  - Inspección centralizada
  - Automatización completa
  - Compliance integrado

Una VPC avanzada no busca flexibilidad infinita, busca:

  • Claridad: entender qué habla con qué en segundos
  • Aislamiento: blast radius contenido
  • Escalabilidad controlada: crece sin caos
  • Seguridad por defecto: deny unless explicitly allowed

Cuando llegas aquí, ya no estás “usando AWS”. Estás diseñando sistemas.