feat(mail): Z-Push Exchange ActiveSync gateway for mobile clients

Wraps Stalwart in EAS so iOS/Android native Mail/Calendar 'Exchange'
accounts get two-way mail+calendar+contacts sync (BackendCombined:
IMAP + CalDAV /dav/cal/%l/ + CardDAV, credentials pass through).

- services/zpush: Z-Push 2.6.4 (AGPLv3, see LICENSE-NOTES.md) on
  php:8.2-apache-bookworm (trixie dropped libc-client); PHP 8 sysv
  sprintf fatal sed-patched; autodiscover dispatcher answers
  mobilesync schema, proxies outlook schema to Stalwart unchanged
- prod: zpush Deployment (replicas:1, Recreate — file sync state),
  /Microsoft-Server-ActiveSync Ingress on mail.dezky.eu (no redirect,
  POST-heavy), autodiscover.dezky.eu repointed to the dispatcher,
  selectorless stalwart-imaps/-smtps Services (host-Stalwart is
  implicit-TLS only: 993/465, no plain 143/587 — verified on node1)
- CI: build+deploy zpush like the other apps

EAS tops out at 14.1: covers native mobile clients, NOT the Outlook
mobile app (needs 16.1) and not new Outlook for Windows (no EAS).
This commit is contained in:
Ronni Baslund
2026-06-12 11:12:11 +02:00
parent 2e3c0f9188
commit 58a2c8077d
16 changed files with 658 additions and 13 deletions
@@ -1,9 +1,13 @@
# Mail-client autodiscovery for dezky.eu — routes ONLY the discovery paths
# through Traefik to host-Stalwart's HTTP listener (10.42.0.1:8080):
# Mail-client autodiscovery for dezky.eu — routes ONLY the discovery paths:
#
# autodiscover.dezky.eu POST /autodiscover/autodiscover.xml (Outlook)
# autodiscover.dezky.eu POST /autodiscover/autodiscover.xml (Outlook + EAS)
# autoconfig.dezky.eu GET /mail/config-v1.1.xml (Thunderbird)
#
# autodiscover.dezky.eu goes to the zpush dispatcher (zpush.yaml), which
# answers mobilesync-schema requests itself (Exchange ActiveSync devices)
# and proxies outlook-schema (mail) requests through to host-Stalwart's
# HTTP listener unchanged. autoconfig.dezky.eu still hits Stalwart directly.
#
# Everything else on these hostnames (Stalwart's /admin, /login, /jmap …)
# falls through to Traefik's 404 — the management surface stays internal.
# No HTTPS-redirect middleware on purpose: Thunderbird probes plain HTTP and
@@ -44,6 +48,59 @@ subsets:
- name: http
port: 8080
---
# IMAPS + SMTPS for the zpush EAS gateway (zpush.yaml) — same
# selectorless-Service-to-host pattern as stalwart-http above. Host-Stalwart
# exposes ONLY the implicit-TLS variants (993/465; no plain 143/587 —
# verified on node1 2026-06-12), all bound to the host wildcard, so the
# cni0 gateway address reaches them just like :8080.
apiVersion: v1
kind: Service
metadata:
name: stalwart-imaps
labels:
app.kubernetes.io/name: stalwart-imaps
app.kubernetes.io/part-of: dezky
spec:
ports:
- name: imaps
port: 993
targetPort: 993
---
apiVersion: v1
kind: Endpoints
metadata:
name: stalwart-imaps
subsets:
- addresses:
- ip: 10.42.0.1
ports:
- name: imaps
port: 993
---
apiVersion: v1
kind: Service
metadata:
name: stalwart-smtps
labels:
app.kubernetes.io/name: stalwart-smtps
app.kubernetes.io/part-of: dezky
spec:
ports:
- name: smtps
port: 465
targetPort: 465
---
apiVersion: v1
kind: Endpoints
metadata:
name: stalwart-smtps
subsets:
- addresses:
- ip: 10.42.0.1
ports:
- name: smtps
port: 465
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
@@ -62,13 +119,14 @@ spec:
- host: autodiscover.dezky.eu
http:
paths:
# Outlook probes both capitalizations.
# Outlook probes both capitalizations. zpush dispatches by schema:
# mobilesync → Z-Push, outlook (mail) → proxied to Stalwart.
- path: /autodiscover/autodiscover.xml
pathType: Exact
backend: { service: { name: stalwart-http, port: { number: 8080 } } }
backend: { service: { name: zpush, port: { number: 80 } } }
- path: /Autodiscover/Autodiscover.xml
pathType: Exact
backend: { service: { name: stalwart-http, port: { number: 8080 } } }
backend: { service: { name: zpush, port: { number: 80 } } }
- host: autoconfig.dezky.eu
http:
paths: