#!/bin/bash # Stalwart install hooks. # # Installing a mail server is not like installing any other app: the container # starting successfully means almost nothing. Mail only works once DNS, reverse # DNS and outbound port 25 are right, and every one of those lives OUTSIDE the # box — at the registrar and the VPS provider. So the job of these hooks is to # say plainly what still has to be done, with the actual values to enter, rather # than reporting "installed" and leaving the admin to discover weeks later that # their mail is landing in spam. # Echo the admin credentials for the standard final-message block (word-split # by the caller into positional args: user pass). stalwart_install_message_data() { printf '%s %s' "${CFG_STALWART_ADMIN_USER:-admin}" "${CFG_STALWART_ADMIN_PASSWORD:-}" } stalwart_install_post_start() { local app_name="$1" ((menu_number++)) echo "" echo "---- $menu_number. Mail server checks + the DNS records you still need" echo "" # Resolved admin port comes from the compose tag (format `external:internal`), # the same source adguard's hook reads — the legacy $usedport1 isn't populated # by the current install pipeline. local compose_file="$containers_dir$app_name/docker-compose.yml" local admin_pair admin_port admin_pair=$(tagsManagerGetTagContent "$compose_file" "PORTS_TAG_1" 2>/dev/null) admin_port="${admin_pair%%:*}" # ---- 1. Can this host even send mail? -------------------------------- # Most cheap VPS providers block outbound 25 by default (and several only # unblock on request). A blocked port 25 means no mail EVER leaves the box, # and nothing in the WebUI would otherwise reveal it. isNotice "Checking outbound port 25 (required to deliver mail to other servers)…" if command -v timeout >/dev/null 2>&1 \ && timeout 8 bash -c 'exec 3<>/dev/tcp/gmail-smtp-in.l.google.com/25' 2>/dev/null; then isSuccessful "Outbound port 25 is open." else isError "Outbound port 25 appears BLOCKED or filtered on this host." isNotice " Most VPS providers block it by default. Ask your provider to unblock" isNotice " outbound 25, or mail will queue and never deliver." fi # ---- 2. Reverse DNS -------------------------------------------------- # Receiving servers check that the sending IP resolves back to a name. A # generic provider PTR (e.g. static.1.2.3.4.provider.net) is a common reason # for mail being junked, and it can only be fixed in the provider's panel. if [[ -n "$public_ip_v4" ]] && command -v dig >/dev/null 2>&1; then local ptr; ptr=$(dig +short -x "$public_ip_v4" 2>/dev/null | head -1) if [[ -n "$ptr" ]]; then isNotice "Reverse DNS (PTR) for $public_ip_v4 is: ${ptr%.}" isNotice " It should match your mail hostname. Set it in your VPS provider's panel." else isError "No reverse DNS (PTR) record for $public_ip_v4 — set one at your VPS provider." fi fi # ---- 3. Is the admin console actually there? ------------------------- # Stalwart v0.16 does not ship the WebUI inside the Docker image: the admin # console is a single-page app the server fetches from GitHub on first # start. If this host had no outbound HTTPS at that moment the download # silently fails, the server still comes up healthy, and /admin and # /account answer 404 forever with nothing to explain why. Checking it here # turns "the panel is broken" into a one-line, fixable cause. isNotice "Checking the admin console (WebUI) responds…" local admin_code admin_code=$(runFileOp docker exec stalwart-service curl -fsS -o /dev/null -w '%{http_code}' \ --max-time 5 http://localhost:8080/admin 2>/dev/null | tr -d '\r') if [[ "$admin_code" == "404" ]]; then isError "The admin console is missing (/admin returns 404)." isNotice " Stalwart does not bundle the WebUI — it downloads it from" isNotice " https://github.com/stalwartlabs/webui/releases/latest on first start." isNotice " That download failed, so /admin and /account will 404 until it succeeds." isNotice " Allow outbound HTTPS to github.com from this host, then restart the" isNotice " container: docker restart stalwart-service" isNotice " The mail server itself is unaffected — only the web interface is." elif [[ -z "$admin_code" ]]; then isError "Could not probe the admin console (no response from the container)." else isSuccessful "Admin console is being served (HTTP $admin_code)." fi # ---- 4. The records the admin must add themselves -------------------- # Printed with real values so they can be pasted at the registrar. DKIM is # deliberately NOT guessed here: Stalwart generates the keypair on first # run, and the public key must be copied from its admin UI. local mail_host="${host_setup:-your-mail-hostname}" local mail_domain="${mail_host#*.}" echo "" isNotice "DNS records to add at your domain registrar:" echo " MX @ 10 ${mail_host}" echo " A ${mail_host} ${public_ip_v4:-}" echo " TXT @ \"v=spf1 mx -all\"" echo " TXT _dmarc \"v=DMARC1; p=quarantine; rua=mailto:postmaster@${mail_domain}\"" echo " TXT ._domainkey (DKIM — copy from Stalwart's admin UI once it has" echo " generated the key; it is not known until then)" echo "" # ---- 5. Where to go next --------------------------------------------- if [[ -n "$admin_port" ]]; then isNotice "Finish setup in the admin interface:" [[ -n "$public_ip_v4" ]] && echo " http://$public_ip_v4:$admin_port/" echo "" isNotice "Sign in as '${CFG_STALWART_ADMIN_USER:-admin}' with the password shown below," isNotice "then add your domain and create mailboxes." fi isNotice "Until MX, PTR, SPF, DKIM and DMARC are all in place, expect delivery" isNotice "problems — that is normal for a new mail server, not a fault in the app." }