Qué contiene el inventario de VPC Atlas, los cuatro formatos en que sale y por qué hice el CSV con una fila plana por recurso.
El diagrama es la foto, el inventario es la lista
Un diagrama te cuenta cómo es una red. Sirve poco para responder a «qué subnets son públicas en eu-west-1», a «qué ha cambiado desde el mes pasado» o a «mándame todas las VPC con sus CIDR». En una imagen no se puede buscar, y nadie compara dos a ojo con garantías. Para esas preguntas quería una lista: un único documento con todo lo que encontró el escaneo, que una persona pueda filtrar y un script pueda leer.
VPC Atlas ya escribía ese documento en JSON, Markdown y HTML. La versión 0.7.0 le suma una cuarta forma, el CSV. Aquí cuento qué contiene el inventario, para quién es cada formato, cómo se obtiene, tres maneras de filtrarlo y las decisiones que tomé en el CSV, entre ellas la que te mete un apóstrofo suelto en las celdas.
Qué contiene el documento
El inventario es la cuenta escrita como un árbol. De cada cuenta auditada cuelgan sus regiones. De una región cuelgan sus VPC y los recursos que no están en ninguna VPC, como los Transit Gateways y los peerings. Una VPC contiene sus subnets y los recursos que no están en ninguna subnet, como las tablas de rutas y los security groups. Una subnet contiene lo que se coloca en ella, como los NAT gateways y los balanceadores de carga, y trae su zona de disponibilidad, su CIDR y un nivel: pública cuando la ruta por defecto sale por un internet gateway; si no, privada, aislada o desconocida.
Junto al árbol de red, cada cuenta tiene una capa de servicios en cinco familias: DNS y edge, API y balanceo, cómputo y contenedores, datos y almacenamiento, y servicios de cuenta como CloudTrail y GuardDuty. Cuando el escaneo no pudo leer un valor de los servicios de cuenta, el fichero pone not read. Eso no es lo mismo que cero o no, así que una llamada denegada nunca se lee como una cuenta vacía.
Después vienen las partes que cruzan cuentas. Las cuentas que el escaneo vio pero para las que no tiene credenciales salen como cuentas externas, cada enlace que cruza el límite de una cuenta tiene su propia entrada, y las llamadas que fallaron al descubrir salen como incidencias (issues). Un resumen con los recuentos abre el documento. Todos los recursos llevan los mismos campos: id, nombre, ARN, estado, sus detalles, sus tags, si el dueño es una cuenta que no auditaste y qué cuentas auditadas lo vieron.
Cuatro formatos, cuatro lectores
El JSON, en vpcatlas-inventory.json, es para máquinas y para jq; su identificador de esquema es vpcatlas-inventory/1. El Markdown es para una pull request o una wiki: el mismo documento en tablas, con los detalles cortados a 240 caracteres por celda. El HTML es una sola página autocontenida con un cuadro de búsqueda que filtra las filas por id, nombre, tipo o cuenta, y es el que mandaría a alguien que no va a abrir una terminal. El CSV es para una hoja de cálculo o para csvkit.
Son cuatro versiones de un mismo documento, y el mismo grafo da siempre el mismo documento.
Cómo se obtiene
vpcatlas discover escribe el inventario junto con los diagramas. vpcatlas inventory escanea igual pero no dibuja nada, que es lo que quieres para el anexo de una auditoría o para un job programado:
vpcatlas inventory --profile prod --format json --output-dir out
--format acepta json, md, html, csv o all, repetido o separado por comas, y all son los cuatro. Los ficheros se escriben junto a vpcatlas-graph.json en el directorio de salida, vpcatlas-out por defecto.
El dashboard sirve los mismos cuatro en /export/inventory.json, /export/inventory.md, /export/inventory.html y /export/inventory.csv, y la pestaña de inventario tiene un botón .csv. /export/all.zip trae todo junto: los diagramas, el inventario en los cuatro formatos y graph.json.
Si ya tienes un grafo guardado, no necesitas AWS para nada:
vpcatlas inventory --cache ./out/vpcatlas-graph.json --format csv
Tres maneras de filtrarlo
En una hoja de cálculo, activa el filtro en la fila de cabecera y elige un valor en la columna type, por ejemplo subnet, y luego una región en la columna region. Con eso tienes las subnets de una región.
Con csvkit, la misma pregunta como comando, tal como está en la documentación:
csvgrep -c type -m subnet vpcatlas-out/vpcatlas-inventory.csv | csvcut -c region,vpc_id,id,name,cidr
Con jq, sobre el JSON, las subnets públicas, una por línea con cuenta, región, VPC, subnet, CIDR, zona y nivel. Es la receta de las referencias del agent skill con un select añadido, y INV es la ruta del fichero JSON:
INV=vpcatlas-out/vpcatlas-inventory.json
jq -r '.accounts[] as $a | $a.regions[] as $r | $r.vpcs[] as $v | $v.subnets[]
| select(.tier == "public")
| [$a.account_id, $r.region, $v.vpc.id, .subnet.id, .cidr, .availability_zone, .tier] | @tsv' "$INV"
Por qué el CSV es como es
La primera decisión fue la forma. El JSON es un árbol, y un árbol no cabe en una tabla. La respuesta ordenada parecía un fichero por nivel: cuentas, VPC, subnets, cada uno apuntando a su padre por id. No la construí, porque lo primero que se hace con una hoja de cálculo es filtrar, y un filtro trabaja sobre una sola hoja. Con tres ficheros, «todo lo que hay en esta subnet» se convierte en una búsqueda cruzada entre ficheros, que es justo el trabajo que quería ahorrarte.
Así que es una sola tabla, con una fila por recurso y una columna scope que dice dónde está la fila: region, vpc, subnet o service. Los ids de la VPC y de la subnet se repiten en cada fila que cuelga de ellas, de modo que una fila sigue teniendo sentido cuando has filtrado todo lo demás. El precio son 23 columnas. Las filas van en el orden del documento, la fila de una subnet y luego los recursos que hay en ella. Para un recurso de la capa de servicios, family dice a qué familia pertenece.
Con los detalles y los tags pasa lo contrario. Una subnet y un NAT gateway no comparten atributos, así que una columna por atributo daría una tabla muy ancha y casi vacía. Los dos se escriben como clave=valor; clave=valor, en el orden del documento, y a diferencia del Markdown los detalles no se cortan.
La protección de las celdas salió de lo que hay en ellas. Los tags son texto que puede escribir cualquiera con acceso a la cuenta, y una hoja de cálculo ejecuta una celda que empieza por =. Por eso una celda que empieza por =, +, - o @ recibe un apóstrofo delante, y la hoja enseña el texto en vez de ejecutarlo. También cuentan las formas de ancho completo de esos signos, y un tabulador o un retorno de carro delante de uno se quitan antes, para que no pueda esconderlo. La contrapartida es que una celda que empieza por un signo menos recibe también el apóstrofo, sea inofensiva o no. El apóstrofo forma parte del fichero. Quítalo si lees el CSV desde código.
Lo demás es más pequeño. Las líneas terminan en CRLF, como define el formato CSV. La cabecera se escribe siempre, aunque el escaneo no encuentre ningún recurso, así que un script que lee el fichero por nombre de columna nunca se topa con un fichero vacío. Un valor que falta es una celda vacía, y el mismo documento da los mismos bytes, de modo que dos CSV del mismo grafo salen idénticos. El fichero es UTF-8 sin marca de orden de bytes: si una hoja de cálculo muestra mal los acentos, importa el fichero y elige UTF-8. Una hoja también puede leer un id de cuenta de 12 dígitos como número y quitarle los ceros del principio, así que importa esa columna como texto cuando necesites el id exacto.
Lo que no hace
- El CSV tiene los recursos y nada más. Las cuentas externas, los enlaces entre cuentas y las incidencias no son filas; están en el JSON y en el Markdown.
- El número de filas puede no coincidir con
summary.resourcesdel JSON. Una VPC que se mencionó pero que el escaneo nunca describió tiene una fila de relleno en el árbol, y el resumen no la cuenta. - Un escaneo solo puede nombrar lo que ven sus credenciales. VPC Atlas todavía no descubre las cuentas de una AWS Organization: las das tú, repitiendo
--profileo--role-arn. Si se ejecuta desde la cuenta de administración, lee Organizations solo para ponerles nombre a las cuentas. - Cada fichero es un escaneo, con la hora de ese escaneo en
generated_at. Es una instantánea, no un historial. Para comparar dos momentos guardas dos ficheros de grafo, o dejas que el dashboard registre los escaneos y dibuje la diferencia.
Dime qué falta
El formato está en la documentación del inventario, el comando en la página de inventory y el código en el repositorio. Si echaste en falta una columna, o acabaste escribiendo a mano un filtro, dime cuál.
Top comments (0)