14 KiB
Почта 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.