Custom Postfix SMTP relay image — builds via Kaniko, deploys on hermes
Go to file
sirxavor 9e8c6f7994 chore(metodo): reparto — un arnes con ruta a un scratchpad no corre
Rail nuevo en §1, medido en el banco de subiquity del motor de hermes-infra:
commiteado con rutas absolutas al scratchpad de otra sesion. Tenia casa y no
arrancaba. Y su aviso era cierto del codigo y falso de las rutas.

Solo .metodo/ — byte a byte desde la matriz.

Sesion: superadmin

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 15:59:44 +02:00
.metodo chore(metodo): reparto — un arnes con ruta a un scratchpad no corre 2026-09-02 15:59:44 +02:00
doc docs(metodo): commitear la copia vendorizada + doc/ de tres capas 2026-08-15 03:01:14 +02:00
.gitattributes chore(metodo): re-estampado 2026-08-23 (A43: el > de blockquote · D102: la casilla) + bloque eol=lf acotado 2026-08-23 15:08:39 +02:00
CLAUDE.md chore(metodo): el puntero al semaforo pasa a estamparse 2026-08-30 02:32:24 +02:00
Dockerfile Llevar a la imagen los cuatro arreglos del incidente del 2026-07-29 2026-07-29 22:43:50 +02:00
entrypoint.sh Llevar a la imagen los cuatro arreglos del incidente del 2026-07-29 2026-07-29 22:43:50 +02:00
main.cf Llevar a la imagen los cuatro arreglos del incidente del 2026-07-29 2026-07-29 22:43:50 +02:00
master.cf Llevar a la imagen los cuatro arreglos del incidente del 2026-07-29 2026-07-29 22:43:50 +02:00
README.md docs: por que la config es como es, y fuera el CI muerto 2026-07-29 23:12:05 +02:00
smtpd.conf feat(smtp-relay): initial custom postfix+sasl relay image 2026-05-20 20:25:04 +00:00

smtp-relay

Postfix SMTP relay with Cyrus SASL authentication and TLS.

Used by Mailu (personal + Solidaria NGO) on valhalla to route outbound mail through hermes, which has a trusted residential IP accepted by Gmail and Hotmail.

Image: harbor.manabo.org/library/smtp-relay
Deployed on: hermes (clusters/hermes/smtp-relay/ in asgard)


Build

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

⚠️ 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.


Configuration

Required Vault secrets (app/smtp-relay/smtp-relay-sasl)

Key Description
relay_user SASL username (e.g. relayuser)
relay_pass SASL password (plaintext — stored in Vault)
relay_domain SASL domain (e.g. manabo.org)

TLS (certs/smtp-relay-tls)

Wildcard cert for relay.manabo.org — pushed to Vault via PushSecret on valhalla.

Env vars (from ExternalSecret)

Var Source
RELAY_AUTH_USER relay_user
RELAY_AUTH_PASS relay_pass
RELAY_AUTH_DOMAIN relay_domain

How it works

  • Listens on ports 25 (SMTP, TLS optional) and 587 (submission, TLS required)
  • Uses hostNetwork: true — ports exposed directly on the hermes host IP
  • Entrypoint creates a sasldb2 user from the env vars on every start
  • Only clients authenticated via SASL can relay mail
  • TLS cert mounted from Vault ExternalSecret

Gotchas

  • sasldb2 recreated on every restart: credentials are read from env vars and 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.