From 6a10eab342a477846ab5f3e67f8ab17169f12807 Mon Sep 17 00:00:00 2001 From: sirxavor Date: Wed, 29 Jul 2026 23:12:05 +0200 Subject: [PATCH] docs: por que la config es como es, y fuera el CI muerto El README describia un flujo que no existe: ./build.sh con Kaniko y un workflow de Gitea Actions. Kaniko y Gitea Actions se estan retirando, y ademas este workflow llevaba roto desde mayo -- era el unico del arbol que usaba actions/checkout@v4, que necesita node, y el runner :host no lo tiene. Fallaba en 70 s en cada push sin que nadie lo mirara: la imagen que sostuvo el correo 70 dias era del 20 de mayo. Un CI que falla siempre y en silencio es peor que no tener CI, asi que se borra en vez de dejarlo de adorno. Se documenta el build con mcp-build (con el gotcha del image_name sin host, que duplica el registro y muere con 401) y, sobre todo, POR QUE cada linea de la config es la que es. Las cuatro que no se pueden tocar sin reabrir el incidente llevan su sintoma exacto al lado, para que el siguiente que las vea raras sepa lo que cuesta "limpiarlas": tlsmgr, el grupo sasl, el realm desde RELAY_AUTH_DOMAIN y chroot=n. Y queda escrito como se prueba un cambio de esta imagen ANTES de desplegarlo, que aqui no es opcional: desplegar es reiniciar el unico pod que sostiene el correo, y si falla no hay alerta que lo diga porque la alerta ES el correo. Co-Authored-By: Claude Opus 5 --- .gitea/workflows/build.yml | 23 -------- README.md | 50 +++++++++++++++-- scripts/build.sh | 106 ------------------------------------- 3 files changed, 46 insertions(+), 133 deletions(-) delete mode 100644 .gitea/workflows/build.yml delete mode 100644 scripts/build.sh diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml deleted file mode 100644 index a6dd5f3..0000000 --- a/.gitea/workflows/build.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Build smtp-relay - -on: - push: - branches: [main] - paths: - - 'Dockerfile' - - 'entrypoint.sh' - - 'main.cf' - - 'master.cf' - - 'smtpd.conf' - - 'scripts/build.sh' - - '.gitea/workflows/build.yml' - workflow_dispatch: - -jobs: - build: - runs-on: [self-hosted, valhalla] - steps: - - uses: actions/checkout@v4 - - - name: Build image (dev) - run: bash scripts/build.sh dev diff --git a/README.md b/README.md index 60e0833..8ceee55 100644 --- a/README.md +++ b/README.md @@ -12,12 +12,22 @@ Deployed on: hermes (`clusters/hermes/smtp-relay/` in asgard) ## Build -```bash -./build.sh 1.0.0 +Con **mcp-build** (2026-07-29). Antes había un `scripts/build.sh` con Kaniko y un +`.gitea/workflows/build.yml`: **los dos se han borrado**. Kaniko y Gitea Actions se están +retirando, y este workflow además llevaba roto desde mayo — era el único del árbol que usaba +`actions/checkout@v4`, que necesita `node`, y el runner `:host` no lo tiene +(`Cannot find: node in PATH`). Fallaba en 70 s en cada push y **nadie se enteraba**: la imagen que +sostuvo el correo durante 70 días era del 20 de mayo. + +``` +trigger_build(repo_url="https://git.manabo.org/xavor/smtp-relay.git", + ref="main", image_tag="dev", image_name="library/smtp-relay") ``` -Packages the Dockerfile context, uploads to MinIO, runs Kaniko in-cluster on valhalla, -and pushes the resulting image to Harbor. +⚠️ `image_name` va **sin el host**. Con `harbor.manabo.org/library/smtp-relay` el destino sale +duplicado (`harbor.manabo.org/harbor.manabo.org/...`) y el push muere con `401`. + +El despliegue se pinea a mano en `asgard/clusters/hermes/smtp-relay/kustomization.yaml`. --- @@ -61,3 +71,35 @@ Wildcard cert for `relay.manabo.org` — pushed to Vault via PushSecret on valha `saslpasswd2` re-creates the sasldb. This is intentional (stateless SASL). - **No DKIM**: DKIM signing is not implemented in this image. Relay delivers mail as-is; DKIM signatures must be added by the sending MTA (Mailu). + +### Por qué la config es como es (incidente del 2026-07-29) + +El correo saliente de `manabo.org` estuvo **semanas sin salir** por cinco fallos encadenados, cada +uno tapando al siguiente. Cuatro de los arreglos vivieron 70 días aplicados a mano sobre el +contenedor vivo, fuera de git. Están en la imagen desde el 2026-07-29. Relato completo en +`asgard/clusters/hermes/smtp-relay/README.md`. **Lo que no se puede tocar sin reabrirlo:** + +- **`tlsmgr` en `master.cf`.** Sin ese daemon, `smtpd` **anuncia** `STARTTLS` en el `EHLO` y luego + no puede hacerlo: `454 4.7.0 TLS not available due to local problem`. La capacidad anunciada no + prueba la capacidad. +- **`postfix` en el grupo `sasl`** (`usermod -aG sasl postfix` en el Dockerfile). `/etc/sasldb2` es + `root:sasl 0640`; sin el grupo, `smtpd` no puede abrirlo → `454 Temporary authentication failure`. +- **`smtpd_sasl_local_domain` sale del `entrypoint`, no de `main.cf`.** Tiene que ser el mismo + `RELAY_AUTH_DOMAIN` con el que `saslpasswd2` crea la entrada del sasldb. Estaba declarado aparte + como `$myhostname`, así que el realm era `relayuser@relay.manabo.org` y la entrada real + `relayuser@manabo.org`. **No lo devuelvas a `main.cf`**: separados, vuelven a poder divergir. +- **`chroot = n` en todos los servicios de `master.cf`.** Con chroot, Postfix necesita + `/var/spool/postfix/etc/resolv.conf` poblado en el build; sin él **no resuelve MX** y el correo se + queda en cola con `Name service error`, aunque `getent hosts` dentro del contenedor sí resuelva + (la herramienta de diagnóstico no ve lo mismo que el consumidor). En un contenedor el chroot no + aporta nada: el contenedor **es** el jail. +- **`postlogd` + `maillog_file`.** Postfix escribe a syslog y aquí no corre ninguno ⇒ `kubectl logs` + sale **vacío**. Sin esto, el incidente se diagnostica a ciegas — y es lo que permite ver si un + despliegue futuro sale mal. + +**Cómo se prueba un cambio de esta imagen antes de desplegarlo** (obligatorio: desplegar aquí es +reiniciar el único pod que sostiene el correo, y si falla **no hay alerta que lo diga, porque la +alerta es el correo**): arranca la imagen candidata aparte con el entrypoint real y las env de +producción, y exige `diff` vacío de `postconf -n` y `postconf -M` contra el contenedor en marcha. +Un diff limpio no basta: ejercita también `STARTTLS` y `AUTH PLAIN` contra ella (`235 +Authentication successful`), que es lo que un diff de configuración no caza. diff --git a/scripts/build.sh b/scripts/build.sh deleted file mode 100644 index 1171081..0000000 --- a/scripts/build.sh +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env bash -# scripts/build.sh [tag] -# Empaqueta el contexto, sube a MinIO, lanza Kaniko en-cluster, espera. -set -euo pipefail - -TAG="${1:-dev}" -HARBOR="harbor.manabo.org" -IMAGE="${HARBOR}/library/smtp-relay:${TAG}" -BUCKET="kaniko-builds" -CONTEXT_KEY="smtp-relay/context.tar.gz" - -echo "==> Building ${IMAGE}" - -echo "==> Packaging context ..." -tar -czf /tmp/kaniko-context.tar.gz \ - --exclude='.git' \ - --exclude='scripts' \ - --exclude='.gitea' \ - --exclude='README.md' \ - -C "$(git rev-parse --show-toplevel)" \ - Dockerfile entrypoint.sh main.cf master.cf smtpd.conf - -echo "==> Uploading to MinIO ..." -mc cp /tmp/kaniko-context.tar.gz "minio/${BUCKET}/${CONTEXT_KEY}" -rm /tmp/kaniko-context.tar.gz - -JOB_NAME="kaniko-smtp-relay-$(date +%s)" -echo "==> Launching Kaniko job: ${JOB_NAME}" - -cat < Waiting for build (timeout 10m) ..." -kubectl wait "job/${JOB_NAME}" -n kaniko \ - --for=condition=complete \ - --timeout=600s || { - echo "==> Build FAILED. Logs:" - POD=$(kubectl get pods -n kaniko -l "job-name=${JOB_NAME}" -o name | head -1) - kubectl logs -n kaniko "$POD" --all-containers - kubectl delete "job/${JOB_NAME}" -n kaniko --ignore-not-found - exit 1 -} - -echo "==> Done: ${IMAGE}"