Files
k8s-manifests/tools/README.md
T
chemavxandClaude Opus 4.8 7b50e4f382 tools: la auditoría de manifiestos, en un temporizador semanal
Sábados 07:00 UTC como servicio de host en chemavx-k8, fuera del cluster, igual
que seo-watch y el canario de hermes. Separado a propósito de esos dos (domingo
06:00 y lunes 05:30): si algo falla, se sabe cuál sin desenredar tres avisos de
la misma mañana.

Callado por diseño: sólo escribe a Telegram si hay hallazgos nuevos Y cambian
respecto a la semana anterior. Si la deriva sigue igual porque aún no se ha
arreglado, no insiste — un aviso semanal que siempre llega deja de leerse, que
es el fallo que se ha estado corrigiendo estos días.

Rompen el silencio cuatro cosas: deriva nueva; el repo local por detrás de
origin (se habría comparado contra ficheros viejos); excepciones que ya no se
disparan; y que el propio auditor haya fallado, porque un vigilante roto no es
un "todo bien".

Probado viéndolo hacer las cuatro, no sólo la buena, y por el camino real
(systemctl start, con el entorno del unit, no desde mi shell). Dos cosas que
salieron de probarlo:

- La primera versión avisaba de "cambios sin commitear". Es estado de trabajo
  normal y la copia local es justo la intención que hay que comparar, así que
  habría mandado ruido cada semana. Ahora eso es contexto, no motivo.
- Un error de sintaxis en el auditor hace que Python salga con código 1, EL
  MISMO que "hay deriva": el aviso habría dicho "los ficheros no coinciden" con
  el volcado de una traza. Ya no basta el código de salida, se exige además que
  el informe traiga su línea de resumen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:46:46 +00:00

105 lines
4.8 KiB
Markdown

# tools/
## `audita-exports.py` — ¿miente este repo?
Compara cada manifiesto con el objeto que hay desplegado de verdad.
```bash
python3 tools/audita-exports.py # informe (sale 1 si hay algo nuevo)
python3 tools/audita-exports.py -v # incluye las excepciones y su motivo
python3 tools/audita-exports.py -d gitea # sólo esos directorios
```
### Por qué existe
**Sólo mira los directorios que no gestiona ArgoCD.** En los que sí, ArgoCD ya
compara git contra el cluster cada 3 minutos y lo marca OutOfSync; ahí la deriva
no puede esconderse. En los demás —`monitoring`, `argocd`, `authentik`, `gitea`,
`homarr`, `vaultwarden`, `cloudflare-ddns`, `cluster-wide`…— **nadie comprueba
nada**: git es documentación, y una documentación desfasada es peor que no
tenerla, porque se aplica creyendo que restaura y en realidad rompe.
La primera pasada (2026-07-22) encontró, sobre 175 objetos:
- `argocd/deployment-argocd-redis.yaml` no llevaba el anclaje al control-plane ni
los límites de recursos. Aplicarlo deshacía el arreglo del Exit Code 255.
- A `gitea/configmap-daemon-json.yaml` le faltaban dos `insecure-registries` que
sí estaban vivos: aplicarlo dejaba a la CI sin poder subir imágenes.
- El ConfigMap `buildkitd-config` llevaba 90 días desplegado sin manifiesto.
- Siete imágenes con tag flotante: git decía `gitea:1.25.5` y el cluster corría
**1.27.0**, subido solo en algún reinicio.
Recordatorio que explica cómo se llega a eso: **en estos directorios un commit no
despliega nada.** Hay que editar el fichero *y* `kubectl apply`.
### Cómo leer la salida
| Tipo | Significa |
|---|---|
| `DIFIERE` | el fichero afirma algo que ya no es cierto |
| `SOLO VIVO` | hay algo desplegado que el fichero no menciona |
| `NO DESPLEGADO` | el fichero describe un objeto que no existe |
Lo normal es terminar en `hallazgos NUEVOS: 0`. Si sale algo, o el cluster se
tocó a mano sin actualizar el fichero (regenéralo), o el fichero se cambió sin
aplicarlo (aplícalo), o es una excepción legítima (justifícala abajo).
### `excepciones-auditoria.yaml`
Un informe que siempre saca ruido deja de leerse, así que las excepciones
conocidas se declaran ahí **con su motivo**. El script avisa cuando una excepción
ya no se dispara: significa que aquello se arregló y la entrada sobra.
## Temporizador semanal
`audita-semanal.py` pasa la auditoría y decide si molestar. Instalado como
servicio de host en chemavx-k8 (**fuera del cluster**, como seo-watch y el
canario de hermes):
```bash
sudo cp tools/audita-exports.{service,timer} /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now audita-exports.timer
systemctl list-timers audita-exports.timer # sábados 07:00 UTC
sudo systemctl start audita-exports.service # forzar una pasada
```
**Callado por diseño.** Sólo escribe a Telegram si hay hallazgos nuevos *y*
cambian respecto a la semana anterior: si la deriva sigue igual porque aún no se
ha arreglado, no repite. Un aviso que llega todas las semanas deja de leerse.
Qué sí rompe el silencio:
| Situación | Aviso |
|---|---|
| Deriva nueva | 🟠 con el informe |
| El repo local por detrás de `origin` | 🟠/🟡 — se comparó contra ficheros viejos |
| Excepciones que ya no se disparan | 🟡 — sobran, bórralas |
| El propio auditor ha fallado | 🔴 — un vigilante roto no es un "todo bien" |
Tener cambios **sin commitear** no avisa: es estado de trabajo normal y la copia
local es justamente la intención que hay que comparar. Sale como contexto entre
paréntesis cuando ya se avisa por otra cosa.
Probado viéndolo hacer las cuatro cosas, no sólo la buena: con el repo limpio se
calla; con una deriva inyectada manda el aviso; repetida la misma deriva no
insiste; y con el auditor roto a propósito (error de sintaxis y muerte temprana)
manda el 🔴. Ojo con ese último caso: un error de sintaxis en Python sale con
código 1, **el mismo que "hay deriva"**, así que no basta mirar el código de
salida — se exige además que el informe traiga su línea de resumen.
El token de Telegram sale del secret `grafana-telegram-infisical` (ns
`monitoring`), el mismo que usan seo-watch y hermes-bot. Al venir de Infisical,
una rotación llega sola a los tres y no hay copia en disco.
### Lo que NO hace
No compara valores de `Secret` —sólo nombres de clave—, se salta lo que ignora
`.gitignore` (los `kube-root-ca.crt` y ServiceAccounts `default` autogenerados) y
no toca nada: es de sólo lectura.
Un aviso por experiencia propia: el `imageID` de un pod **no siempre es el digest
del registro**. Si viene como `repo@sha256:…` lo es; si es un `sha256:…` pelado
es el id local de containerd y **no se puede descargar** — fijar una imagen a ese
valor da `ImagePullBackOff`. Para comparar imágenes, `repoDigest` contra
`repoDigest`, con `sudo k3s crictl inspecti <ref>`.