Перейти к содержанию

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

В данном гайде будет использован бесплатный почтовый сервер Stalwart в виде докер-образа, но никто не мешает взять любой другой.

Дальше по тексту я буду использовать следующие "плейсхолдеры":

  • yourserver.com - домен вашего сервера. Поменяйте на свой.
  • 87.XXX.YYY.ZZZ - IP-адрес вашего сервера. Поменяйте на свой.
  • {{your_django_app_name}} - название вашего django-приложения. Не проекта, а именно приложения!

Предустановки

Я предполагаю, что у вас на сервере уже установлены docker и docker compose самой свежей версии.

Также нам потребуется swaks - консольный клиент для проверки отправки почты.

Bash
1
sudo apt install swaks

Для создания сертификатов нам также потребуется certbot.

Bash
1
sudo apt install certbot

Установка Stalwart

  1. Создаём каталог, где будем хранить конфиги нашего почтового сервера, например:
Bash
1
2
mkdir -p /srv/selfhosted/stalwart/conf
cd /srv/selfhosted/stalwart
  1. Добавляем в этот каталог файл docker-compose.yaml со следующим содержимым:
YAML
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
services:
  stalwart:
    container_name: "stalwart"
    image: "stalwartlabs/stalwart:latest"
    ports:
      - 18443:443   # HTTPS порт (18443) для доступа к веб-интерфейсу Stalwart
      - 18080:8080  # HTTP порт (18080) для доступа к веб-интерфейсу Stalwart
      - 10025:25
      - 587:587     # НЕ МЕНЯЕМ
      - 465:465     # НЕ МЕНЯЕМ
      - 143:143
      - 10993:993
      - 4190:4190
      - 110:110
      - 995:995
    volumes:
      - /srv/selfhosted/stalwart/conf:/opt/stalwart

  # Убрать, если не нужен web-клиент для тестирования работоспособности отправки почты.
  roundcube:
      container_name: "roundcube"
      image: "roundcube/roundcubemail:latest"
      environment:
        - ROUNDCUBEMAIL_DEFAULT_HOST=tls://stalwart  # tls: 587 порт
        - ROUNDCUBEMAIL_SMTP_SERVER=tls://stalwart   # tls: 587 порт
      ports:
        - 12080:80   # По этому порту (12080) будет доступен webmail-клиент Roundcube. На нём можно протестировать отправку почты.

В официальной документации предлагается запускать Stalwart командой docker run, что не всегда удобно, т.к. для изменения конфигурации приходится менять команду, поэтому я предпочитаю вариант с использованием docker-compose.yaml.

  1. Запускаем сервисы.
Bash
1
docker compose up -d
  1. Переходим в официальную документацию и следуем дальше уже по ней https://stalw.art/docs/install/platform/docker/

Настройка Stalwart

  1. Переходим на http://87.XXX.YYY.ZZZ:18080 и проходим первоначальную настройку.
  2. На этом этапе я предполагаю, что вы уже самостоятельно настроили DNS записи по туториалу и mail.yourserver.com выдаёт корректный IP адрес.
Bash
1
2
dig +short mail.yourserver.com
87.XXX.YYY.ZZZ
  1. Также не забудьте настроить PTR-запись у своего VPS/VDS/Bare-metal провайдера. Некоторые почтовые провайдеры (такие как Gmail) не принимают письма от почтовых серверов без PTR-записи.

    Обратитесь к нему с запросом: "Установите PTR-запись для IP 87.XXX.YYY.ZZZ на mail.yourserver.com" Проверить успешную установку записи можно командой:

    Bash
    1
    2
    dig -x 87.XXX.YYY.ZZZ +short
    mail.yourserver.com.
    
  2. Теперь, когда наш почтовый домен резолвится по DNS, мы можем сгенерировать TLS-сертификаты.

Bash
1
certbot certonly -d mail.yourserver.com

Забираем их из каталога /etc/letsencrypt/live/mail.yourcerver.com.

Там должны лежать сгенерированные certbot файлы. Нас интересуют в первую очередь:

  • fullchain.pem - CA+Certificate.
  • privkey.pem - Certificate private key.

Переходим в панель управления Stalwart - http://87.XXX.YYY.ZZZ:18080/settings/certificate и создаём новый сертификат.

В появившейся форме:

  • Certificate Id - пишем любое понятное вам название, желательно разделяя слова нижним подчёркиванием. Неплохой пример Certificate Id: mail_yourserver_com_letsencrypt_cert_default

  • Certificate: Сюда копируем весь вывод команды

Bash
1
cat /etc/letsencrypt/live/mail.yourserver.com/fullchain.pem
  • Private Key: Сюда копируем весь вывод команды
Bash
1
cat /etc/letsencrypt/live/mail.yourserver.com/privkey.pem
  • Default certificate: Если у нас один единственный сертификат на почтовый сервер - включаем.

После чего нажимаем кнопку Save & Reload и все изменения у нас автоматически применяются.

Проверка отправки почты

Bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
swaks \
  --to "[email protected]" \
  --from "[email protected]" \
  --server "mail.yourserver.com" \
  --port 587 \
  --auth LOGIN \
  --auth-user "[email protected]" \
  --auth-password '@noreply@' \
  --tls \
  --body "TEST from swaks"

Если после выполнения команды у вас пришло письмо от [email protected] на ваш почтовый ящик [email protected] - почтовый сервер работает.

Бонус: Настройка DJANGO проекта на отправку почты с вашего почтового сервера

Добавьте в settings.py вашего проекта следующие строки:

Python
1
2
3
4
5
6
7
EMAIL_BACKEND = "django.core.mail.backends.smtp.EmailBackend"
EMAIL_HOST = "mail.yourserver.com"
EMAIL_PORT = 587
EMAIL_HOST_USER = "[email protected]"
EMAIL_HOST_PASSWORD = "@noreply@"
EMAIL_SUBJECT_PREFIX = "[YOUR SERVER]: "
EMAIL_USE_TLS = True
Внимание!

Я не рекомендую вам явно прописывать все значения в settings.py. Вместо этого воспользуйтесь готовым решением типа django-configurations и загружайте значения для этих переменных из dotenv-файла.

Также создайте в любом из ваших django apps следующий файл:

Bash
1
2
3
4
# Создаём каталог для management commands
mkdir -p {{your_django_app_name}}/management/commands/
# Добавляем туда файл с определением новой management command
touch {{your_django_app_name}}/management/commands/send_test_email.py

Со следующим содержимым:

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
from django.core.mail import send_mail
from django.core.management.base import BaseCommand
from django.conf import settings


class Command(BaseCommand):
    help = "Sending test email"

    def add_arguments(self, parser):
        parser.add_argument("-f", "--from", action="store", required=False, default="[email protected]")
        parser.add_argument("-t", "--to", action="store", required=True)

    def handle(self, *args, **options):
        test_email_params = dict(
            subject="Test Email from {{your_django_app_name}}",
            message="Test Email sent from django app {{your_django_app_name}}.",
            from_email=options.get('from'),
            recipient_list=[options.get('to')],
            fail_silently=False,
            auth_user=settings.EMAIL_HOST_USER,
            auth_password=settings.EMAIL_HOST_PASSWORD,
        )
        self.stdout.write(
            self.style.NOTICE(f"Sending test email with params:\n{test_email_params}\n")
        )
        send_mail(**test_email_params)
        self.stdout.write(
            self.style.SUCCESS(f"Email successfully sent, check your email box (also check spam folder): {options.get('to')}\n")
        )

Теперь вся отправка почты Django-приложения будет работать через ваш почтовый сервер!