Ctrl K
ComunidadGuías…

deploy/configure-relay.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python3
"""Point Stalwart's outbound delivery at the smarthost defined in .env.

Outbound :25 is blocked by the host provider, so remote mail cannot go direct
to MX. This creates a Relay route and makes it the default outbound path.

Idempotent: re-running updates the existing route rather than adding another.
"""
import sys
from _stalwart import load_env, client

ROUTE_NAME = "raymail-relay"

env = load_env()
missing = [k for k in ("RELAY_HOST", "RELAY_USERNAME", "RELAY_PASSWORD") if not env.get(k)]
if missing:
    sys.exit(f"Set {', '.join(missing)} in /root/raymail/.env first — nothing changed.")

call = client(env)

route = {
    "@type": "Relay",
    "name": ROUTE_NAME,
    "description": "Outbound smarthost (direct :25 egress is blocked)",
    "address": env["RELAY_HOST"],
    "port": int(env.get("RELAY_PORT", 587)),
    "protocol": "smtp",
    "allowInvalidCerts": False,
    # 587 is STARTTLS, so implicit TLS stays off. Set true only for port 465.
    "implicitTls": int(env.get("RELAY_PORT", 587)) == 465,
    "authUsername": env["RELAY_USERNAME"],
    # authSecret uses the SecretKeyOptional union: the variant is "Value"
    # (not "Text", which is what x:DkimSignature/x:Certificate use).
    "authSecret": {"@type": "Value", "secret": env["RELAY_PASSWORD"]},
}

existing = next(
    (r for r in call("x:MtaRoute/get", {"ids": None}).get("list", [])
     if r.get("name") == ROUTE_NAME),
    None,
)

if existing:
    res = call("x:MtaRoute/set", {"update": {existing["id"]: route}})
    if res.get("notUpdated"):
        sys.exit(f"Route update rejected: {res['notUpdated']}")
    print(f"updated relay route -> {env['RELAY_HOST']}:{env.get('RELAY_PORT', 587)}")
else:
    res = call("x:MtaRoute/set", {"create": {"r": route}})
    if res.get("notCreated"):
        sys.exit(f"Route create rejected: {res['notCreated']}")
    print(f"created relay route -> {env['RELAY_HOST']}:{env.get('RELAY_PORT', 587)}")

# The outbound strategy is a singleton. Values are Stalwart *expressions*, so
# a literal route name has to be single-quoted.
#
# The local branch is not optional: without it every message — including
# locally-addressed mail and the DSNs Stalwart generates for its own bounces —
# is pushed at the smarthost, which rejects them and double-bounces.
res = call("x:MtaOutboundStrategy/set", {"update": {"singleton": {"route": {
    "match": {"0": {"if": "is_local_domain(rcpt_domain)", "then": "'local'"}},
    "else": f"'{ROUTE_NAME}'",
}}}})
if res.get("notUpdated"):
    sys.exit(f"Outbound strategy rejected: {res['notUpdated']}")

print("outbound strategy now routes all remote mail through the relay")
print("restart to apply:  docker restart raymail-stalwart")