В моменте идея развертывания дома почтового сервера может показаться излишеством, ведь вполне для отправки различного рода уведомлений можно использовать Telegram или любой другой мессенджер, а для переписок со своего домена почту можно завернуть на какой-нибудь почтовый сервис, предлагающий возможность использования своего домена бесплатно, например mail.ru. Однако с учетом последних телодвижений РКН в сторону блокировки Telegram и ограничение в 5 ящиков на mail.ru (а с недавнего времени и использование сторонних почтовых клиентов только на платных тарифах), вариант использования собственного почтового сервера кажется все более интересной альтернативой.
Сама по себе настройка классического почтового сервера из связки Postfix и Dovecot на Linux процедура весьма нетривиальная. Существуют и чуть более простые альтернативы, например Exim, но и там процедура настройки весьма непростая. И что делать в ситуации когда хочется получить почтовый сервер с минимум телодвижений, но при этом еще и развернуть его в уже имеющимся дома Docker’e или кластере Kubernetes?
И тут на помощью приходит Mailu — простой в настройке, полностью функциональный почтовый сервер, который нацелен для использования в контейнерной среде.
Ранее на Хабре уже были статьи по развертыванию Mailu, но все они касались именно развертыванию в Docker’е. В данном руководстве я постараюсь максимально кратко изложить настройку на примере своего домашнего кластера k8s.
Нам потребуется:
-
наличие собственного домена и возможности управления DNS записями для него (я использую домен от reg.ru), для примера буду использовать yourdomain.ru
-
«белый» ip-адрес, так же для примера это будет 1.2.3.4
-
готовность вашего провайдера сделать обратную (PTR) запись для вашего белого ip
-
развернутый кластер Kubernetes, настроенный helm
-
CSI и соответствующий StorageClass
-
возможность раздавать внешние ip-адреса в кластере k8s через l2advertisements (это умеет например CNI Cillium или MetalLB в связки с другим CNI)
-
наличие настроенных ingress, cert-manager’a и автоматизации получения сертификата LetsEncrypt (в моем случае это ClusterIssuer для reg.ru от Флант)
Подготовка
Для начала создади�� DNS запись типа А с поддоменом mail, итоговая запись будет выглядеть как:
1.2.3.4 А mail.yourdomain.ru
Далее направляем письмо вашему провайдеру с просьбой создать обратную запись.
Так же нужно создать MX:
@ MX mail.yourdomain.ru
После того, как nslookup 1.2.3.4 вернет заветный ответ mail.yourdomain.ru можно приступать к установке.
Развертывание
Рекомендую сразу создать отдельный namespace, т. к. почтовому серверу в любом случае потребуется открыть хотя бы порт 21/TCP из вне для обмена почтой, а любой открытый во вне порт — это потенциальная уязвимость. Поэтому вынесем почту в отдельный namespace:
kubectl create namespace dmz-mailu
И сразу обрежем в этом неймспейсе все лишнее, создадим network-policy-dmz-mail.yaml со следующим содержимом:
### Block all by default
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-dmz-mail-deny
namespace: dmz-mail
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
### Allow dmz-mail outgoing ports
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-mailu-ports
namespace: dmz-mail
spec:
podSelector: {}
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 21
- protocol: TCP
port: 465
Эта политика заблокирует в неймспейсе любой доступ, кроме указанных портов. Применим наш файл:
kubectl -n dmz-mail apply -f network-policy-dmz-mail.yaml
Теперь добавим репозиторий mailu в helm:
helm repo add mailu https://mailu.github.io/helm-charts/
Далее нам потребуется values для чарта:
helm show values mailu/mailu > mailu_values.yaml
Минимальный values.yaml выглядит примерно так:
hostnames:
- mail.youdomain.ru
domain: youdomain.ru
timezone: "Europe/Moscow"
## тут задается создание первого аккаунта, пароль от него будет сгенерирован
initialAccount:
enabled: true
username: "mail-admin-user"
domain: "youdomain.ru"
mode: "ifmissing"
## сверьте на всякий случай с сеть для pod'ов
subnet: 10.42.0.0/16
## я использовал Postgres из чарта, но вы можете так же использовать MariaDB или внешнюю БД
postgresql:
enabled: true
auth:
enablePostgresUser: true
postgresPassword: "SomeStrongPassword"
username: mailu
password: "AnotherStrongPassword"
primary:
persistence:
enabled: true
storageClass: "your-storageClass"
## Настройки хранилища для вашего почтового сервера
persistence:
single_pvc: true # используем один PVC для Postfix, Dovecot, etc
size: 50Gi
storageClass: "your_storageclass"
ingress:
ingressClassName: "nginx"
annotations: {cert-manager.io/cluster-issuer: regru-dns} # для автогенерации TLS сертификата
extraHosts:
- name: mail-local.youdomain.ru #Зачем нам дополнительный хост расскажу ниже
path: /
extraTls:
- hosts:
- mail-local.youdomain.ru
secretName: mail.local-tls
front:
hostPort:
enabled: false # в принципе можете попробовать использовать hostport, но это в данном руководстве этого касаться не буду
externalService:
enabled: true
type: LoadBalancer
loadBalancerIP: 192.168.10.200 # тут укажите ip из пула адресов для L2Advertisment
externalTrafficPolicy: Local
## если есть еще один, более быстрый StorageClass, то возможно стоит разместить redis на нем
redis:
master:
persistence:
enabled: true
size: 4Gi
storageClass: "your-storageClass-nvme"
## rsamd не стоит отключать, т.к. письма в любом случае будут идти через него
rspamd:
enabled: true
## а вот clamav вполне можно для экономии ресурсов
clamav:
enabled: false
## и OleTools - софт для анализа офисных файлов
oletools:
enabled: false
## как и Tika - он отвечает за обработку метаданных
tika:
enabled: false
После правки под свой домен и кластер примените файл:
helm install mailu mailu/mailu -n dmz-mailu --values mailu_values.yaml
После того, как все поды в namespace перейду в статус Running мы можем добавить еще одну сущность — service для фронта. В принципе он и не должен требоваться, т. к. есть Ingress, но из-за того, что Mailu хочет контролировать TLS сам, терминация между Ingres и подом mailu-front происходит некорректно и получается ошибка SSL, обойти которую мне не удалось (возможно я что-то сделал не так, буду рад помощи в комментариях). Поэтому было применено решение «в лоб» - просто вытянть фронт на отдельный ip.
Создадим файл svc-mailu-front-web.yaml:
apiVersion: v1
kind: Service
metadata:
name: svc-mailu-front-web
namespace: dmz-mail
spec:
externalTrafficPolicy: Local
ports:
- port: 443
protocol: TCP
targetPort: 443
selector:
app.kubernetes.io/component: front
app.kubernetes.io/instance: mailu
app.kubernetes.io/name: mailu
type: LoadBalancer
loadBalancerIP: 192.168.10.201 # тут укажите еще один ip из пула адресов для L2Advertisment
и применим его:
kubectl -n dmz-mail apply -f svc-mailu-front-web.yaml
Остается только связать на вашем локальном DNS сервере (или просто в hosts) запись mail-local.youdomain.ru и ip 192.168.1.230. После этого заходим на mail-local.youdomain.ru, логинимся данными админа из values.yaml и переходим в webadmin.
Так же потребуется прокинуть 21й порт с вашего шлюза до loadBalancerIP из values.yaml.
DKIM, DMARC, SPIF
Уже на этом этапе можно создать пару пользователей и покидаться письмами между собой, но этого нам явно мало, ведь хотелось бы еще общаться с получателями во вне. Мы конечно можем попробовать отправить письмо например на гуглопочту, но моментально получим отказ, т. к. указанные выше записи у нас просто отсутствуют. Но добавить их не составит труда.
Переходим в Administration — Mail Domains — Ваш домен — Редактировать, а затем в правом верхнем углу нажимамем Generate Keys. В итоге увидим примермно следующее:

Далее нам необходимо перенести недостающие записи в ваш DNS:

Через некоторое время (как публичные DNS серверы начнут отдавать TXT или после успешной проверки на https://mxtoolbox.com/dkim.aspx ) можно попробовать покидаться почтой с почтовыми ящиками на публичных серверах. Письма могут попасть в спам на почтовых сервисах, поэтому если что-то не пришло, то стоит проверить нежелательную почту.
Заключение
Не берусь оценивать строго, насколько рационально, да и вообще разумно ли, держать почтовый сервер в kubenetes, да и вообще сможет ли Mailu заменить полновесный почтарь (все же использование докера накладывает ограниченияусложнения в конкурировании). Но в целом как простой способ рассылать уведомления со своих сервисов или для аналогичных небольших задач данное решение на мой взгляд вполне имеет место быть.
Если у кого-есть опыт использования похожих решений, буду рад прочитать в комментариях.
Автор: riobravo_ru
