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}"