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.
PrivateLink: exponer servicios sin abrir redes
Cuando varios equipos, cuentas o incluso empresas necesitan consumir servicios internos, AWS PrivateLink es la solución elegante.
El problema sin PrivateLink:
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
La solución: PrivateLink
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
Configuración PrivateLink (Provider):
# 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
}
Configuración PrivateLink (Consumer):
# 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
}
}
Casos de uso PrivateLink:
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.