Files
moonwell-web/docs/mail-server.md
T

14 KiB
Raw Blame History

Почта MoonWell

Для support@moon-well.online и no-reply@moon-well.online используется отдельный контейнер Stalwart Mail Server. Он предоставляет SMTP, IMAP, антиспам, DKIM и административный интерфейс. Данные и конфигурация хранятся в именованных Docker volumes и переживают пересоздание контейнера.

Что требуется от сервера

  • постоянный публичный IPv4-адрес;
  • возможность принимать TCP-соединения на порты 25, 465, 587, 993;
  • разрешённый исходящий TCP-порт 25;
  • PTR/rDNS для IP со значением mail.moon-well.online;
  • доступ к управлению DNS-зоной moon-well.online.

Без корректного PTR исходящие сообщения часто попадают в спам. Некоторые хостинги блокируют исходящий порт 25 — это нужно проверить до настройки.

Первый запуск

./scripts/mail-server.sh init

Проверь значения в .env.mail. Затем создай DNS-запись:

mail.moon-well.online.  A  <публичный IPv4 сервера>

Запусти сервер:

./scripts/mail-server.sh up
./scripts/mail-server.sh credentials

Административный HTTP-порт намеренно доступен только на loopback-интерфейсе. Открой туннель со своего компьютера:

ssh -L 8081:127.0.0.1:8081 <пользователь>@<сервер>

После этого открой http://127.0.0.1:8081/admin и войди с временными реквизитами из команды credentials.

Мастер Stalwart

Используй следующие значения:

  • Server hostname: mail.moon-well.online
  • Default email domain: moon-well.online
  • Automatically obtain TLS certificate: включено
  • Generate email signing keys: включено
  • Storage: локальный RocksDB
  • Directory: Internal Directory
  • Logging: Console

Если DNS размещён у поддерживаемого Stalwart провайдера, подключи его в последнем шаге. Тогда сертификат выпускается через DNS-01 без занятия публичного порта 443. Иначе понадобится отдельно передать серверу TLS-сертификат или временно предоставить ему порт 443 для ACME-проверки.

Сохрани постоянные реквизиты администратора, показанные мастером, и перезапусти контейнер:

./scripts/mail-server.sh restart

Создай обычный пользовательский аккаунт support@moon-well.online. Административный аккаунт нельзя использовать в почтовом клиенте.

Для приложения создай отдельный SMTP-аккаунт no-reply@moon-well.online с правом отправки от этого адреса. Laravel использует его для подтверждения email и восстановления пароля. Пароль хранится только в .env приложения; в репозиторий его добавлять не нужно. Ящик поддержки остаётся отдельным.

В панели Stalwart открой Management → Directory → Accounts и создай обычный пользовательский аккаунт (роль User):

  • имя: no-reply;
  • домен: moon-well.online;
  • основной email: no-reply@moon-well.online;
  • отображаемое имя: MoonWell;
  • пароль: отдельный случайный пароль для SMTP.

В версиях панели с полем полного логина укажи no-reply@moon-well.online. Названия полей могут отличаться между версиями; итоговый адрес и SMTP-логин должны быть no-reply@moon-well.online. Не выдавай этому аккаунту роль администратора. Отдельные MX/SPF/DKIM-записи для нового ящика не нужны — используется уже настроенный домен.

Создание аккаунтов в документации Stalwart.

DNS после мастера

В панели Stalwart открой:

Management → Domains → Domains → moon-well.online → View DNS Zone file

Перенеси предложенные записи в DNS-панель. В обязательный минимум входят:

  • MX;
  • SPF;
  • DKIM;
  • DMARC;
  • записи автоконфигурации, которые предложит Stalwart.

Значение DKIM нужно брать только из созданной установки: ключ генерируется индивидуально. Не копируй DKIM из примеров.

Настройка почтового клиента

Адрес:               support@moon-well.online
IMAP-сервер:         mail.moon-well.online
IMAP-порт:           993
Шифрование IMAP:     SSL/TLS
SMTP-сервер:         mail.moon-well.online
SMTP-порт:           465
Шифрование SMTP:     SSL/TLS
SMTP-аутентификация: включена
Логин:               support@moon-well.online

Подключение Laravel

После создания no-reply@moon-well.online укажи в production .env:

APP_URL=https://moon-well.online
MAIL_MAILER=smtp
MAIL_SCHEME=smtps
MAIL_HOST=mail.moon-well.online
MAIL_PORT=465
MAIL_USERNAME=no-reply@moon-well.online
MAIL_PASSWORD=<пароль ящика>
MAIL_FROM_ADDRESS=no-reply@moon-well.online
MAIL_FROM_NAME="${APP_NAME}"

Затем сбрось и пересобери кэш конфигурации:

docker compose -f compose.prod.yaml exec -T --user app app php artisan optimize:clear
docker compose -f compose.prod.yaml exec -T --user app app php artisan config:cache

Если используется SSH-туннель к БД ядра, добавь -f compose.prod.ssh-tunnel.yaml к обеим командам. Для Sail используй docker compose exec -T laravel.test php artisan ….

MAIL_HOST — публичное имя с действительным TLS-сертификатом. Приложение может отправлять письма на опубликованный Stalwart порт 465, даже если его контейнер находится в другой Docker-сети. Не отключай проверку сертификата и не подменяй имя на stalwart при использовании публичного сертификата.

При доставке нового кода выполни миграции (php artisan migrate --force). Новые регистрации на сайте и через API лаунчера требуют подтверждения почты; существующие и импортированные аккаунты сохраняют доступ. Подтверждение не требует предварительного входа. Повторное письмо доступно на /email/verify, восстановление — на /forgot-password. Обе ссылки действуют 60 минут; ссылка восстановления одноразовая. Пароль меняется в web.users, как при смене пароля в кабинете; игровые SRP-данные остаются под управлением механизма билетов лаунчера.

Локально можно оставить MAIL_MAILER=log: тогда письма записываются в лог. Для проверки реальной доставки используй свой аккаунт: зарегистрируй его, подтверди почту, затем запроси восстановление. В обоих письмах отправитель должен быть MoonWell <no-reply@moon-well.online>, а ссылки вести на APP_URL.

Логотип сайта, писем и отправителя

Иконки сайта находятся в public/branding/, многоразмерный favicon — в public/favicon.ico. Шаблон уведомлений использует public/branding/moonwell-mail-logo.png как встроенное CID-изображение: логотип прикрепляется к письму, а не загружается с чужого сервера.

Для значка рядом с отправителем подготовлен векторный логотип SVG Tiny P/S: https://moon-well.online/branding/moonwell-bimi.svg. В DNS-панели REG.RU добавляется запись:

Тип:      TXT
Имя:      default._bimi
Значение: v=BIMI1; l=https://moon-well.online/branding/moonwell-bimi.svg;

Полное имя записи — default._bimi.moon-well.online. Нужен только один BIMI TXT для этого имени. Запись применяется ко всем отправителям домена, включая support и no-reply. Публиковать её следует после загрузки SVG на production. Текущая политика DMARC p=reject соответствует требованию BIMI; ослаблять её не требуется.

Это самостоятельная BIMI-запись без сертификата: наличие записи не гарантирует отображение логотипа у получателя. Gmail требует дополнительно VMC или CMC от удостоверяющего центра. После выпуска сертификата его цепочка PEM размещается по HTTPS, а в TXT добавляется a=https://…/certificate.pem. Обычный TLS-сертификат сайта или SMTP не заменяет VMC/CMC.

Требования: Google Workspace, формат SVG BIMI Group.

Проверка после публикации DNS:

dig +short TXT default._bimi.moon-well.online
curl --fail --head https://moon-well.online/branding/moonwell-bimi.svg

Резервное копирование

В бэкап должны попадать оба именованных volume:

  • stalwart-config;
  • stalwart-data.

Перед восстановлением используй ту же minor-версию образа Stalwart, что указана в compose.mail.yaml.

TLS на production-сервере

На production сертификат выпускает системный Certbot через Nginx. Контейнер получает read-only каталог MAIL_TLS_DIR и использует:

/run/stalwart-tls/fullchain.pem
/run/stalwart-tls/privkey.pem

Deploy-hook Certbot копирует обновлённые файлы в этот каталог и перезапускает только контейнер Stalwart, чтобы он перечитал сертификат.

Hook устанавливается из scripts/deploy-mail-certificate.sh.

Certbot запускает deploy-hook для каждого обновлённого сертификата. Скрипт проверяет, что сертификат покрывает mail.moon-well.online (MAIL_TLS_HOSTNAME), и пропускает сертификаты других сайтов. Иначе обновление сертификата основного сайта подменит сертификат SMTP/IMAP.

Внешний HTTPS-доступ к панели и JMAP проксируется через Nginx. Готовая конфигурация находится в ops/nginx-mail.conf; внутренний HTTP-порт Stalwart остаётся доступен только на 127.0.0.1.

После установки сертификата его нужно добавить в Stalwart и выбрать как сертификат по умолчанию. Для HTTP-настроек за Nginx также включается useXForwarded, чтобы автоматические блокировки применялись к реальному адресу клиента, а не к Docker gateway.

Если Nginx возвращает 502, а Stalwart работает и пишет security.ip-blocked ... remoteIp = <Docker gateway>, проверь блокировку адреса шлюза Docker в Settings → Security → Blocked IPs. Сними блокировку только с этого адреса и добавь именно его в Allowed IPs, затем перезагрузи настройки. Адрес шлюза можно узнать через docker inspect. HTTP-порт Stalwart должен оставаться доступным только на loopback-интерфейсе хоста; Nginx передаёт реальный адрес клиента в HTTP-заголовках. Не добавляй весь интернет в разрешённые адреса и не отключай auto-ban.