Настройка Self-Hosted почтового сервера Postgresq/Postfix/Dovecot и т.п.

Оцените статью

Пожалуй начнем

Настройка PostfixAdmin

PostfixAdmin это вообще какой-то привет из темного прошлого, но ничего лучше не завезли. Поэтому будем пользоваться тем, что дали и бесплатно.

Как показывает практика, самым простым решением для управления, пользователями, паролями и алиасами является Postfix и давайте тогда не будет отходить от этой практики. PostfixAdmin это обычное PHP-приложение и для подготовки виртуального хоста и соответствующего окружения мы можем использовать например инструкцию от NextCloud

  • За основу берем стабильный релиз (двух недельной давности в моем случае) из официального гита https://github.com/postfixadmin/postfixadmin
  • Создаем базу данных и пользователя (на хосте rw)
  • Генерируем Setup password
# php -r 'echo password_hash("xxxSecretPasswordxxx", PASSWORD_DEFAULT);'

Пример virtual-host для запуска:

server {
    listen 80;
    server_name postfixadmin.interlan.xyz;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name postfixadmin.interlan.xyz;

    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
    add_header Referrer-Policy no-referrer always;

    # Path to the root of your installation
    root /var/www/vhosts/postfixadmin.interlan.xyz/public/;

    access_log  /var/log/nginx/postfixadmin.interlan.xyz-access.log;
    error_log  /var/log/nginx/postfixadmin.interlan.xyz-error.log warn;

    ssl_certificate /etc/letsencrypt/live/postfixadmin.interlan.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/postfixadmin.interlan.xyz/privkey.pem;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;

    client_max_body_size 1024M;
    fastcgi_buffers 64 4K;

    proxy_connect_timeout 6000;
    proxy_send_timeout 6000;
    proxy_read_timeout 6000;
    send_timeout 6000;

    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    if ($host ~ ^www\.(?<domain>.+)$) {
      return  301 $scheme://$domain$request_uri;
    }

    location = /robots.txt {
        allow all;
    }

    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php {
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_param PATH_INFO $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        fastcgi_param front_controller_active true;
        fastcgi_pass php-handler;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    # Prevents caching of css/less/js/images, only use this in development
    location ~* \.(css|less|js|jpg|png|gif)$ {
        add_header Cache-Control "no-cache, no-store, must-revalidate";
        add_header Pragma "no-cache";
        expires 0;
    }

}

Конфигурационный файл /var/www/vhosts/postfixadmin.interlan.xyz/config.local.php

<?php
$CONF['database_type'] = 'pgsql';
$CONF['database_host'] = '127.0.0.1';
$CONF['database_user'] = 'postfixadmin_user';
$CONF['database_password'] = 'xxxDBUSERPASSWORDxxx';
$CONF['database_name'] = 'postfixadmin_db';
$CONF['encrypt'] = 'md5crypt';
$CONF['configured'] = true;
$CONF['setup_password'] = 'xxxINSTALLxxxHASHxxx';
$CONF['default_aliases'] = array (
    'abuse' => 'chernousov@interlan.xyz',
    'hostmaster' => 'chernousov@interlan.xyz',
    'postmaster' => 'chernousov@interlan.xyz',
    'webmaster' => 'chernousov@interlan.xyz'
);
?>

Переходим по URL https://postfixadmin.interlan.xyz/setup.php (это пример конечно) и выполняем настройку (наполнение схемы базы производится автоматически) и создаем аккаунт администратора.

  • Поле Админ, это адрес электронной почты
  • Дополнительно устанавливаем расширение IMAP для php
    # aptitude install php8.1-imap
  • Симлинк на templates_c
    # ln -s ./templates/ ./templates_c

Теперь у нас есть хранилище для учеток пользователей, доменов, алиасов и т.п. и можем переходить к следующему этапу и сделаем хранилище для почты и возможность подключаться почтовым клиентом.

Установка и настройка Dovecot

Изначально хотел использовать DBmail, но из за больших задержек между датацентрами это будет полная боль и страдание. Посему хранилка будет файловая на GlusterFS для возможности масштабирования.

Пакеты для Dovecot и плагинов устанавливаем на пакете с репликой базы данных.

# aptitude install dovecot-antispam dovecot-lmtpd dovecot-managesieved dovecot-pgsql dovecot-sieve

Настройка Dovecot.

За основу берем базовые конфигурационные файл и вносим ряд правок.

conf.d/10-auth.conf b/conf.d/10-auth.conf
Отключаем  include auth-system.conf.ext (это механизмы системной авторизации) и добавляем:

!include auth-sql.conf.ext

/conf.d/10-mail.conf

Каталог где храним данные мэйлбоксов и UID/GID собственно Dovecot

mail_location = maildir:/opt/mail/%d/%u/
mail_uid = 118
mail_gid = 122
first_valid_uid = 118
last_valid_uid = 118
first_valid_gid = 122
last_valid_gid = 122

/conf.d/10-master.conf
Настраиваем сервисы LMTP и авторизации на работу как через сокет, так и по порту

inet_listener lmtp {
    address = 127.0.0.1
    port = 24
}
unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    mode = 0666
    user = postfix
    group = postfix
}

/conf.d/10-ssl.conf
Настройка Dovecot на использование Let’s encrypt

ssl_cert = </etc/letsencrypt/live/mail.bds.su/fullchain.pem
ssl_key = </etc/letsencrypt/live/mail.bds.su/privkey.pem

conf.d/15-lda.conf
Настройка lmtp сервиса

postmaster_address = chernousov@interlan.xyz
hostname = mail.bds.su
lda_mailbox_autocreate = yes
protocol lda {
  mail_plugins = $mail_plugins
}

conf.d/15-mailboxes.conf
Автосоздание каталогов в IMAP-папках

mailbox Drafts {
     special_use = \Drafts
    auto = subscribe
}
mailbox Junk {
     special_use = \Junk
    auto = subscribe
}
mailbox Trash {
     special_use = \Trash
    auto = subscribe
}

conf.d/20-lmtp.conf
LMTP-сервис переводим на работу по порту вместо сокета

protocol lmtp {
  mail_plugins = $mail_plugins
}
service lmtp {
   inet_listener lmtp {
      address = 127.0.0.1
      port = 24
}

dovecot-sql.conf.ext
Настройки авторизации на использование базы данных

driver = pgsql
connect = host=127.0.0.1 dbname=postfixadmin_db user=postfixadmin_user password=xxxPASSWORDxxx
default_pass_scheme = MD5-CRYPT
password_query = SELECT password FROM mailbox WHERE username = '%u'
user_query = select CONCAT('/opt/mail','/',LOWER(maildir)) as home,  118 AS uid, 122 AS gid FROM mailbox WHERE username = '%u';

dovecot.conf

listen = *

Разрешаем доступ к портам через UFW.

# ufw allow 143
# ufw allow 993

Создаем каталог для хранения почты и меняем владельца и группу.

# mkdir /mnt/gluster-storage/mail/
# chown dovecot:dovecot /mnt/gluster-storage/mail

Перезапускаем и активируем.

# systemctl restart dovecot            
# systemctl enable dovecot

Отладка авторизации.

Проверяем, что пользователь существует:

# doveadm user chernousov@interlan.xyz

Проверяем авторизацию:

# doveadm auth login chernousov@interlan.xyz xxxPASSWORDxxx
passdb: chernousov@interlan.xyz auth succeeded
extra fields:
 user=chernousov@interlan.xyz
userdb extra fields:
 chernousov@interlan.xyz
 home=/mnt/gluster-storage/mail/interlan.xyz/chernousov/
 uid=118
 gid=122
 auth_mech=PLAIN

Если все ок, то пробуем подключиться почтовым клиентом.

Настройка Postfix для приема и отправки электронной почты

Postfix это такая-же классика как и Dovecot, поэтому будем использовать его. Как мне видится можно даже сделать небольшую заметку как вообще вся эта связка работает, но в каком-то минимальном формате исключительно для лэндинга.

Устанавливаем по старой схеме необходимые пакеты.

# apt-get install postfix postfix-pgsql postfix-gld postfix-policyd-spf-python
# aptitude install opendkim opendkim-tools

Подготовка базы почтовых алиасов:

# newaliases
# postfix reload

Устанавливаем и настраиваем два дополнительных сервиса:

DKIM настроим по инструкции для mail-relay

Postgregrey подойдет в дефолтной поставке:

# apt-get install postgrey
# systemctl enable postgrey
# systemctl start postgrey

Дополнительные DNS-записи для доменов:

  • _dmarc TXT «v=DMARC1; p=reject; rua=mailto:chernousov@interlan.xyz»
  • @ TXT «v=spf1 +a +mx -all»

Настройка Firewall:

# ufw allow 25
# ufw allow 465
# ufw allow 587

Пример конфигурации:

myorigin = mail.bds.su
smtpd_banner = $myhostname ESMTP $mail_name (Ubuntu)
biff = no
append_dot_mydomain = no
delay_warning_time = 15m
readme_directory = no
compatibility_level = 3.6

smtpd_tls_cert_file=/etc/letsencrypt/live/mail.bds.su/fullchain.pem
smtpd_tls_key_file=/etc/letsencrypt/live/mail.bds.su/privkey.pem
smtpd_tls_security_level=may
smtp_tls_CApath=/etc/ssl/certs
smtp_tls_security_level=may
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination

smtpd_recipient_restrictions =
        permit_mynetworks
        permit_sasl_authenticated
        reject_unauth_destination
        check_policy_service inet:localhost:10023

myhostname = mail.bds.su
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
mydestination = localhost

relayhost =
mynetworks = 127.0.0.0/8
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = ipv4

virtual_alias_maps = proxy:pgsql:/etc/postfix/virtual_alias_maps.cf
virtual_mailbox_domains = proxy:pgsql:/etc/postfix/virtual_domains_maps.cf
virtual_mailbox_maps = proxy:pgsql:/etc/postfix/virtual_mailbox_maps.cf

virtual_transport = lmtp:127.0.0.1:24

smtpd_sasl_auth_enable = yes
smtpd_sasl_exceptions_networks = $mynetworks
smtpd_sasl_security_options = noanonymous
broken_sasl_auth_clients = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth

smtp_use_tls = yes
smtpd_use_tls = yes
smtpd_tls_auth_only = yes
smtpd_helo_required = yes

milter_default_action = accept
milter_protocol = 2
smtpd_milters = inet:localhost:10021
non_smtpd_milters = inet:localhost:10021

SQL-конфиги:

/etc/postfix/virtual_alias_maps.cf

user= postfixadmin_user
password= xxxPASSWORDxxx
hosts= localhost
dbname= postfixadmin_db
query= SELECT goto FROM alias WHERE address='%s' AND active='1'

/etc/postfix/virtual_domains_maps.cf

user= postfixadmin_user
password= xxxPASSWORDxxx
hosts= localhost
dbname= postfixadmin_db
query= SELECT domain FROM domain WHERE domain='%u'

/etc/postfix/virtual_mailbox_maps.cf

user= postfixadmin_user
password= xxxPASSWORDxxx
hosts= localhost
dbname= postfixadmin_db
query= SELECT goto FROM alias WHERE address='%s' AND active='1'

Отладка

Просмотр текущей очереди:

# mailq

Форсировать очередь:

# mailq -q

Подтверждение что мы не спамеры

 Публичные сервисы типа gmail стали очень злые и нервные и теперь для использования своего self-hosted почтового сервера придется немного поприседать и побить в бубен.

Итак, давайте разбираться, что мы можем сделать.

Google

У гугла как обычно все через одно место и в 90% случаев словим ошибку:

gmail-smtp-in.l.google.com[173.194.215.26] said: 550-5.7.1 [45.138.27.6]
    The IP youre using to send mail is not authorized to 550-5.7.1 send email
    directly to our servers. Please use the SMTP relay at your 550-5.7.1
    service provider instead. Learn more at 5505.7.1

И вот тут все не просто.

  • Идем добавлять домены https://postmaster.google.com/managedomains?pli=1
  • Добавляем DNS-запись подтверждения домена (TXT)
  • Они не быстрые и синхронизация даже после подтверждения займет много времени

Yandex

Здесь обычный GreyList и первое письмо будет уходить долго, а дальнейшие уходят на ура.

Mail.ru

Проверяет DKIM и SPF и все норм

Немного красоты

Веб-интерфейс для доступа к почте будем делать на базе RoundCube. Это обычное PHP-приложение, достаточно функциональное и удобное.

Скачиваем версию Complete на официальной странице загрузок проекта https://roundcube.net/download/

Виртуальный хост:

server {
    listen 80;
    server_name webmail.bds.su;

    # Enforce HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443      ssl http2;

    server_name webmail.bds.su;

    access_log  /var/log/nginx/webmail.bds.su-access.log;
    error_log  /var/log/nginx/webmail.bds.su-error.log warn;

    ssl_certificate /etc/letsencrypt/live/webmail.bds.su/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/webmail.bds.su/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    add_header Strict-Transport-Security "max-age=15768000; includeSubDomains; preload;" always;

    client_max_body_size 64M;
    fastcgi_buffers 64 4K;

    proxy_connect_timeout 600;
    proxy_send_timeout 600;
    proxy_read_timeout 600;
    send_timeout 600;

    gzip on;
    gzip_vary on;
    gzip_comp_level 4;
    gzip_min_length 256;
    gzip_proxied expired no-cache no-store private no_last_modified no_etag auth;
    gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy;

    add_header Referrer-Policy                      "no-referrer"   always;
    add_header X-Content-Type-Options               "nosniff"       always;
    add_header X-Download-Options                   "noopen"        always;
    add_header X-Frame-Options                      "SAMEORIGIN"    always;
    add_header X-Permitted-Cross-Domain-Policies    "none"          always;
    add_header X-Robots-Tag                         "none"          always;
    add_header X-XSS-Protection                     "1; mode=block" always;

    fastcgi_hide_header X-Powered-By;

    root /var/www/vhosts/webmail.bds.su/;

    location / {
       index index.php;
       try_files $uri $uri/ /index.php;
    }

    location ~* \.php$ {
         fastcgi_split_path_info ^(.+?\.php)(/.*)$;
         if (!-f $document_root$fastcgi_script_name) {return 404;}
         fastcgi_pass  unix:/run/php/php8.1-fpm.sock;
         fastcgi_index index.php;
         include fastcgi_params;
         fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

}
  • Инициализируем схему
  • Конфигурацию
  • И плагинов по вкусу.

Похожие записи

Настройка взаимодействия RED ADM и Windows Active Directory

Сегодня проведем несколько экспериментов по настройке взаимодействия RED ADM и Windows Active Directory. Есть несколько способов настройки доверия для упрощения миграции с решений Microsoft на Российское ПО и сегодня их…

Подробная инструкция по написанию YAML‑файлов для Docker Compose

Так как на севере делать абсолютно нечего, то я продолжаю заниматься саморазвитием 🙂 На этой неделе вспоминаю и углубляю свои знания в Docker. Лучший способ запомнить тему, это вести конспект…

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Читать еще статьи

Настройка взаимодействия RED ADM и Windows Active Directory

Настройка взаимодействия RED ADM и Windows Active Directory

Подробная инструкция по написанию YAML‑файлов для Docker Compose

Подробная инструкция по написанию YAML‑файлов для Docker Compose

Установка основного контроллера домена на базе REDADM

Установка основного контроллера домена на базе REDADM

zVirt работа с шаблонами виртуальных машин

zVirt работа с шаблонами виртуальных машин

Подробная инструкция по работе с томами (volumes) в Docker

Подробная инструкция по работе с томами (volumes) в Docker

Сетевые возможности Docker

Сетевые возможности Docker