Un servicio en el centro: páginas de Transit Gateway, grupos de seguridad y API Gateway
Cómo VPC Atlas 0.9.0 dibuja un servicio por página, con sus tablas y hallazgos, y lo que esas páginas todavía no cuentan.
Las preguntas llegaban de una en una
En una revisión reciente de la red, cada pregunta trataba de un solo servicio. ¿A dónde va esta ruta del Transit Gateway? ¿Qué security group está abierto al mundo y cuál no tiene nada asociado? ¿Dónde acaba cada ruta de esta API? VPC Atlas contesta con la página de región, y la página de región lo dibuja todo. La respuesta a cada una de esas preguntas está ahí, pero desparramada por toda la página.
La versión 0.9.0, publicada el 20 de octubre de 2026, añade --focus: una página por servicio, unas tablas junto al inventario y tres hallazgos que la herramienta marca en esas páginas y tablas. Hay además un cambio pequeño que no tiene que ver con focus: las páginas de VPC ahora muestran en el subtítulo las IP libres de cada subnet, como en 10.0.1.0/24 | 251 free IPs.
La opción
pipx install vpcatlas # o: curl -fsSL https://vpcatlas.com/install.sh | sh
vpcatlas discover -r eu-west-1 --focus tgw,sg,apigw --output-dir vpcatlas-out
--focus VIEW funciona en discover, inventory, diagram y serve. Se puede repetir o separar las vistas con comas. Hay tres, y se aceptan alias como transit-gateway y api-gateway.
tgw escribe una página por Transit Gateway, tuyo o compartido contigo, con sus tablas de rutas, sus rutas y sus attachments (uno compartido se dibuja con lo que tus credenciales pueden leer, ver los límites más abajo). sg escribe una página por VPC, con sus security groups, sus reglas y lo que tienen asociado. apigw escribe una página por región, con sus API, endpoints, dominios y destinos de integración. Las páginas salen en draw.io y Lucid como diagrams/vpcatlas-focus-<view>-<id>.<ext>, con el mismo motor de layout que las demás (páginas focus).
Con --focus, --level genera por defecto solo las páginas focus; si le pasas --level account, vpc o all, se añaden esas páginas. Sin --focus no cambia nada. --detail no toca una página focus, y --tag reduce el grafo antes, así que un recurso que --tag deja fuera tampoco sale en una página focus.
En discover y inventory, --focus escribe además seis tablas junto al inventario: tgw-attachments, tgw-routes, sg-rules, sg-attachments, apigw-apis y apigw-integrations. Cada una es un fichero vpcatlas-focus-<table> en todos los formatos de inventario que hayas elegido (json, md, html, csv). El JSON lleva el id de esquema vpcatlas-focus/1, y las filas van ordenadas para que el mismo grafo dé siempre los mismos bytes. Las columnas están en la sección de tablas focus de la documentación.
Tres hallazgos aparecen en las páginas y en las tablas. Los textos de las tarjetas los imprime la herramienta en inglés, tal cual:
open_ingress:<proto>/<ports>, en un security group abierto al mundo (orígenes 0.0.0.0/0 o ::/0) en un puerto que no sea el 80 ni el 443. La tarjeta dice! open to the world: tcp 22.unused, en un grupo que nada usa y que ninguna regla nombra. La tarjeta dice! not attached.default_endpoint_open, en una API con dominio propio cuyo endpointexecute-apipor defecto sigue activo. La tarjeta dice! default endpoint open.
Hay una cuarta marca, en la página del Transit Gateway. Un peering que nadie ha aceptado aparece como pending_acceptance en la tabla de attachments, y su estado va entre paréntesis en la línea que lo une a su caja.
unused no se marca si un servicio que lleva security groups quedó fuera del escaneo con --exclude-services, o no se pudo leer. Un grupo que parece sin uso solo porque el escaneo nunca miró Lambda ni ECS sería una falsa alarma, y prefiero que la herramienta calle a que marque algo equivocado.
Lo que medí
Las cifras salen de la cuenta simulada del repositorio (demo/fake_aws.py, moto 5.2.4, la biblioteca de Python que imita AWS, detrás de un shim pequeño), en eu-west-1, con la 0.9.0 ya publicada. Amplié la cuenta para esta versión, de modo que las tres vistas tuvieran algo que enseñar. Ahora tiene una instancia bastion en una subnet pública con un grupo bastion (SSH abierto al mundo), un grupo legacy-app con RDP abierto al mundo y nada asociado, una API HTTP prod-http y una API REST partner-rest, una VPN dc-vpn conectada al Transit Gateway hub, un peering hub-frankfurt, que nadie ha aceptado, con un Transit Gateway de eu-central-1 en otra cuenta, y una segunda tabla de rutas, hub-partners.
vpcatlas discover -r eu-west-1 --focus tgw,sg,apigw --output-dir vpcatlas-out encuentra 113 recursos y 0 incidencias (ninguna llamada denegada ni fallida), y escribe 5 páginas focus: 1 del Transit Gateway, 3 de security groups (la cuenta tiene 3 VPC) y 1 con las API de la región. Las seis tablas tienen 7 filas en tgw-routes, 4 en tgw-attachments, 20 en sg-rules, 31 en sg-attachments, 2 en apigw-apis y 4 en apigw-integrations.
El Transit Gateway hub (ASN 64512) tiene 4 attachments: la VPC prod, la VPC shared-services, la VPN dc-vpn y el peering, que está en estado pendingAcceptance y lleva el hallazgo pending_acceptance. Su tabla de rutas por defecto tiene 6 rutas: 0.0.0.0/0 → shared-services, 10.0.0.0/16 → prod, 10.1.0.0/16 → shared-services, 10.20.0.0/16 → el peering de Frankfurt, 10.99.0.0/16 como blackhole y 172.16.0.0/12 → dc-vpn. La segunda tabla, hub-partners, tiene una ruta, 10.0.0.0/16 → prod, y el peering está asociado a ella.

hub de la cuenta simulada: sus dos tablas de rutas a la izquierda, sus cuatro attachments a la derecha y el peering sin aceptar con su estado entre paréntesis.«¿A dónde va esta ruta?» ahora es una tarjeta a la izquierda y una línea hasta una caja a la derecha.
En la VPC prod hay 6 security groups (web, app, db, bastion, legacy-app y default) y 15 filas de reglas. bastion lleva open_ingress:tcp/22. legacy-app lleva open_ingress:tcp/3389 y unused. De las 31 filas de sg-attachments, 12 son via_eni, 9 de ellas del grupo default: cuando solo una interfaz de red delata que un grupo está asociado, la página y la tabla nombran la interfaz, no el recurso que hay detrás. La VPC shared-services tiene 2 grupos y 4 reglas, y la tercera VPC 1 grupo y 1 regla.

prod. La página es alta, con una tarjeta por grupo y todos los recursos asociados, así que esto es un recorte. La página completa está a un clic.Cada tarjeta enseña las primeras ocho reglas de entrada y las primeras cuatro de salida de su grupo. Una regla que nombra a otro grupo de la VPC es una línea entre las dos tarjetas, rotulada con el protocolo y los puertos: app acepta el 22 desde bastion, y legacy-app acepta el 8080 desde web.
La región tiene dos API. prod-http es una API HTTP en api.vpcatlas.com con el endpoint por defecto todavía activo, así que lleva default_endpoint_open, y tiene un VPC link, prod-link. partner-rest es una API REST en partners.vpcatlas.com con el endpoint por defecto desactivado y una web ACL regional, prod-api, en su stage. apigw-integrations tiene 4 filas: GET /orders +1 hacia la Lambda prod-api (dos rutas que comparten una integración son una sola fila, y el +1 cuenta la otra), ANY /app/{proxy+} por el VPC link prod-link hasta el balanceador de carga prod-web, GET /status hacia https://status.partner.example/health y POST /orders hacia la Lambda prod-api.

eu-west-1: prod-http lleva la línea ! default endpoint open, y a la derecha están la Lambda, el VPC link con su balanceador y el host externo.Las rutas que no llevaban a ningún sitio
Mientras preparaba estas figuras, las rutas del hub salieron sin destino. Todas decían → -.
El producto pide a AWS las rutas de cada tabla con SearchTransitGatewayRoutes y lee el attachment de cada ruta de la respuesta. El simulador (moto) guarda el attachment de una ruta como un único objeto, mientras que la API real devuelve una lista, así que su serializador lo perdía y cada ruta salía con la lista de attachments vacía. En una cuenta real, las rutas ya tenían su destino desde el principio.
El límite está en el simulador, y ahí lo arreglé: antes de arrancar moto, el script de la demo (demo/fake_aws.py) envuelve la llamada que crea una ruta para que el attachment se guarde como lista.
Un mock que responde con la forma correcta y el contenido equivocado se caza peor que uno que falla, y lo cazaron las tablas: una columna entera de attachment_ids vacíos en tgw-routes salta a la vista más que una flecha que acaba en un guion en una página.
Con un grafo guardado y en el dashboard
--focus sobre un grafo guardado no necesita ninguna llamada a AWS:
vpcatlas diagram --cache vpcatlas-out/vpcatlas-graph.json --focus tgw,apigw
En el dashboard, vpcatlas serve --focus ... o la variable VPCATLAS_FOCUS, separada por comas, añade un tercer grupo a la barra lateral, llamado «Focus views». /api/status informa de las vistas activas en filters.focus. Docker Compose pasa VPCATLAS_FOCUS tal cual, y el chart de Helm acepta config.focus como lista. El resto está en la documentación del dashboard.
Lo que no hace
- Una página focus no tiene capa de cambios en el dashboard: «View changes on diagram» la salta.
- Una vista sin nada que enseñar no escribe página. Sin Transit Gateway no hay página
tgw, y sin API no hay páginaapigw. - Un Transit Gateway compartido por una cuenta que no has escaneado se dibuja con lo que tus credenciales pueden leer: el gateway con su dueño y tus attachments a él. Y una tarjeta de tabla de rutas lista como mucho 12 rutas y termina con
+N more; si AWS anuncia más rutas de las que devuelve, la tarjeta termina con(routes truncated by the scan)yroutes_truncatedvaleyesentgw-routes. La tabla tiene todas las rutas que el escaneo leyó. - Un grupo que solo delata una interfaz se lista a través de esa interfaz (
via_eni). - En la página de API, un balanceador al que se llega por un VPC link y que además es destino directo de otra integración se dibuja una sola vez, y su arista cruza la columna de destinos. Lo dejo para la 0.9.1.
Un hallazgo como open_ingress no siempre es un problema: el bastion de las figuras está abierto en el 22 a propósito.
Dime qué vista toca ahora
La opción está en la documentación del CLI, el historial de versiones en el changelog, y el código en el repositorio y en PyPI. ¿Qué vista querrías después: balanceadores de carga, VPC endpoints, algo que sigues buscando por toda la página de región? Y ¿la regla unused te parece acertada para tus cuentas, o tienes a propósito grupos que nada usa? Dímelo.