Debian + Postfix + Dovecot + Multidomain + SSL + IPv6 + OpenVPN + Multi-interfaces + SpamAssassin-learn + Bind

в 21:51, , рубрики: Debian, devops, dkim, DNS, dovecot, iptables, IPv6, openvpn, postfix, rdns, roundcube, route, spamassassin, spf, SSL, Настройка Linux, Системы обмена сообщениями

Данная статья о том как настроить современный почтовый сервер.
Postfix + Dovecot. SPF + DKIM + rDNS. С IPv6. С шифрованием TSL.
С поддержкой нескольких доменов — часть с настоящим SSL сертификатом.
С антиспам-защитой и высоким антиспам-рейтингом у других почтовых серверов.
С поддержкой нескольких физических интерфейсов.
С OpenVPN, подключение к которому через IPv4, и которое даёт IPv6.
Если вы не хотите изучать эти все технологии, но хотите настроить такой сервер — тогда эта статья для вас.
В статье отсутствуют попытки пояснить каждую деталь. Пояснение идёт к тому, что настроено не стандартно или важно с точки зрения потребителя.

Мотивация настроить почтовый сервер — моя давняя мечта. Может это звучит глупо, но ИМХО, это гораздо лучше, чем мечтать о новой машине любимой марки.
Мотивация настроить IPv6 — две. ИТ специалисту необходимо изучать новые технологии постоянно, чтобы выжить. Хочется внести свой скромный вклад в борьбу с цензурой.
Мотивация настройки OpenVPN — только для того, чтобы IPv6 работал на локальной машине.
Мотивация настройки нескольких физических интерфейсов — у меня на сервере один интерфейс «медленный, но безлимитный», а другой «быстрый, но с тарифом».
Мотивация настройки настройки Bind — мой провайдер предоставляет не стабильный DNS сервер, а google бывает тоже даёт сбои. Хочу стабильный DNS сервер для личного использования.
Мотивация написать статью — черновик был написал 10 месяцев назад, и я в него уже два раза заглядывал. Если даже автору это регулярно надо — то большая вероятность, что и другим понадобится.

Универсального решения для почтового сервера нет. Но я постараюсь написать типа «сделайте вот так и потом, когда всё будет работать как надо — выкиньте лишнее».
Имеется сервер Colocation у компании tech.ru. Есть возможность сравнить с OVH, Hetzner, AWS. Для решения данной задачи гораздо эффективнее будет сотрудничество именно с tech.ru.

На сервере установлен Debian 9.
На сервере 2 интерфейса `eno1` и `eno2`. Первый безлимитный, а второй быстрый соответственно.
Имеется 3 статических IP адреса, XX.XX.XX.X0 и XX.XX.XX.X1 и XX.XX.XX.X2 на интерфейсе `eno1` и XX.XX.XX.X5 на интерфейсе `eno2`.
Имеется XXXX:XXXX:XXXX:XXXX::/64 пул IPv6 адресов, которые назначены на интерфейс `eno1` и из него XXXX:XXXX:XXXX:XXXX:1:2::/96 по моей просьбе назначили на `eno2`.
Имеется 3 домена `domain1.com`, `domain2.com`, `domain3.com`. Для `domain1.com` и `domain3.com` есть SSL сертификат.
Имеется google аккаунт, на который хочется привязать почтовый ящик `vasya.pupkin@domain1.com` (получение почты и отправка почты прямо из gmail интерфейса).
Должен быть почтовый ящик `support@domain2.com`, копию почты с которого я хочу видеть у себя в gmail. И в редко иметь возможность отправить чего-то от имени `support@domain2.com` через web-интерфейс.
Должен быть почтовый ящик `ivanov@domain3.com`, которым будет пользоваться Иванов со своего iPhone.
Отправляемые письма должны соответствовать всем современным требованиям к антиспаму.
Должен быть наивысший уровень шифрования предусмотренный в публичных сетях.
Должна быть поддержка IPv6 и для отправки и для получения писем.
Должен быть SpamAssassin, который никогда не будет удалять письма. А будет или bounce делать или пропускать или отправлять в IMAP папку «Спам».
Должно быть настроено авто-обучение SpamAssassin: если я перемещаю письмо в папку «Спам» — обучится на этом; если я перемещаю письмо из папки «Спам» — обучится на этом. Результаты обучения SpamAssassin — должны влиять на попадаемость письма в папку «Спам».
Скрипты php должны уметь отправлять почту от имени любого домена на данном сервере.
Должен быть openvpn сервис, с возможностью использовать IPv6 на клиенте, у которого нет IPv6.

Сначала надо настроить интерфейсы и маршрутизацию, включая IPv6.
Потом надо будет настроить OpenVPN, который будет соединяться по IPv4 и предоставлять клиенту статический-реальный IPv6 адрес. У этого клиента будет доступ ко всем IPv6 сервисам на сервере и доступ к любым ресурсам IPv6 в интернете.
Потом надо будет настроить Postfix на отправку писем + SPF + DKIM + rDNS и прочие тому подобные мелочи.
Потом надо будет настроить Dovecot и настроить Multidomain.
Потом надо будет настроить SpamAssassin и настроить обучение.
В завершение установить Bind.

============= Multi-interfaces =============

Для настройки интерфейсов надо прописать вот такое в "/etc/network/interfaces".

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
allow-hotplug eno1
iface eno1 inet static
        address XX.XX.XX.X0/24
        gateway XX.XX.XX.1
        dns-nameservers 127.0.0.1 213.248.1.6
        post-up ip route add XX.XX.XX.0/24 dev eno1 src XX.XX.XX.X0 table eno1t
        post-up ip route add default via XX.XX.XX.1 table eno1t
        post-up ip rule add table eno1t from XX.XX.XX.X0
        post-up ip rule add table eno1t to XX.XX.XX.X0

auto eno1:1
iface eno1:1 inet static
address XX.XX.XX.X1
netmask 255.255.255.0
        post-up ip rule add table eno1t from XX.XX.XX.X1
        post-up ip rule add table eno1t to XX.XX.XX.X1
        post-up   ip route add 10.8.0.0/24 dev tun0 src XX.XX.XX.X1 table eno1t
        post-down ip route del 10.8.0.0/24 dev tun0 src XX.XX.XX.X1 table eno1t

auto eno1:2
iface eno1:2 inet static
address XX.XX.XX.X2
netmask 255.255.255.0
        post-up ip rule add table eno1t from XX.XX.XX.X2
        post-up ip rule add table eno1t to XX.XX.XX.X2

iface eno1 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:1::/64
        gateway XXXX:XXXX:XXXX:XXXX::1
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:1:1:1/64 dev $IFACE
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:1:1:2/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:1:1:1/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:1:1:2/64 dev $IFACE

# The secondary network interface
allow-hotplug eno2
iface eno2 inet static
        address XX.XX.XX.X5
        netmask 255.255.255.0
        post-up   ip route add XX.XX.XX.0/24 dev eno2 src XX.XX.XX.X5 table eno2t
        post-up   ip route add default via XX.XX.XX.1 table eno2t
        post-up   ip rule add table eno2t from XX.XX.XX.X5
        post-up   ip rule add table eno2t to XX.XX.XX.X5
        post-up   ip route add 10.8.0.0/24 dev tun0 src XX.XX.XX.X5 table eno2t
        post-down ip route del 10.8.0.0/24 dev tun0 src XX.XX.XX.X5 table eno2t

iface eno2 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:2::/96
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:2:1:1/64 dev $IFACE
        up   ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:2:1:2/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:2:1:1/64 dev $IFACE
        down ip -6 addr del XXXX:XXXX:XXXX:XXXX:1:2:1:2/64 dev $IFACE

# OpenVPN network
iface tun0 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:3::/80

Данные настройки можно применять на любом сервере в tech.ru (с небольшим согласованием с поддержкой) и оно сразу заработает как надо.
Если опыт настройки аналогичных вещей для Hetzner, OVH — там подругому. Сложнее.

eno1 — это название сетевой карты #1 (медленный, но безлимитный).
eno2 — это название сетевой карты #2 (быстрый, но с тарифом).
tun0 — это название виртуальной сетевой карты от OpenVPN.
XX.XX.XX.X0 — IPv4 #1 на eno1.
XX.XX.XX.X1 — IPv4 #2 на eno1.
XX.XX.XX.X2 — IPv4 #3 на eno1.
XX.XX.XX.X5 — IPv4 #1 на eno2.
XX.XX.XX.1 — IPv4 gateway.
XXXX:XXXX:XXXX:XXXX::/64 — IPv6 на весь сервер.
XXXX:XXXX:XXXX:XXXX:1:2::/96 — IPv6 для eno2, всё остальное извне заходит в eno1.
XXXX:XXXX:XXXX:XXXX::1 — IPv6 gateway (стоит отметить, что тут можно/нужно сделать подругому. Указать IPv6 свича).
dns-nameservers — указаны 127.0.0.1 (потому, что установлен bind локально) и 213.248.1.6 (это от tech.ru ).
«table eno1t» и «table eno2t» — cмысл этих route-rule в том, чтобы трафик вошедший через eno1 -> ушёл бы через него же, а трафик вошедший через eno2 -> ушёл бы через него же. А также соединения по инициативе сервера уходили бы через eno1.

ip route add default via XX.XX.XX.1 table eno1t

Этой командой мы задаём, что любой непонятный трафик, который попал под любое rule у которого отмечено «table eno1t» -> направить в интерфейс eno1.

ip route add XX.XX.XX.0/24 dev eno1 src XX.XX.XX.X0 table eno1t

Этой командой мы задаём, что любой трафик по инициативе сервера направить в интерфейс eno1.

ip rule add table eno1t from XX.XX.XX.X0
ip rule add table eno1t to XX.XX.XX.X0

Этой командой мы задаём сами правила маркировки трафика.

auto eno1:2
iface eno1:2 inet static
address XX.XX.XX.X2
netmask 255.255.255.0
        post-up ip rule add table eno1t from XX.XX.XX.X2
        post-up ip rule add table eno1t to XX.XX.XX.X2

Этот блок задаёт второй IPv4 для интерфейса eno1.

ip route add 10.8.0.0/24 dev tun0 src XX.XX.XX.X1 table eno1t

Этой командой мы задаём route от клиентов OpenVPN до локальных IPv4 кроме XX.XX.XX.X0.
Почему этой команды достаточно для всех IPv4 — я до сих пор не понимаю.

iface eno1 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:1::/64
        gateway XXXX:XXXX:XXXX:XXXX::1

Это мы задаём адрес для самого интерфейса. Сервер его будет использовать как «исходящий» адрес. Больше никак использоваться не будет.
Почему указано ":1:1::" так сложно? Чтобы OpenVPN работало правильно и только для этого. Об этом подробнее позже.
На тему gateway — так работает и ладно. Но по правильному — сюда надо указать IPv6 свича к которому подсоединён сервер.
Однако почему-то IPv6 перестаёт работать, если я так делаю. Наверное это заморочки tech.ru какие-то.

ip -6 addr add XXXX:XXXX:XXXX:XXXX:1:1:1:1/64 dev $IFACE

Это добавление IPv6 адреса на интерфейс. Если надо сотню адресов — значит сотню строк в этом файле.

iface eno1 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:1::/64
...
iface eno2 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:2::/96
...
iface tun0 inet6 static
        address XXXX:XXXX:XXXX:XXXX:1:3::/80

Отметил адреса и подсети всех интерфейсов, чтобы было наглядно.
eno1 — обязательно должно быть "/64" — потому что это весь наш pool адресов.
tun0 — подсеть должны быть обязательно больше eno1. Иначе нельзя будет настроить IPv6 gateway для клиентов OpenVPN.
eno2 — подсеть должны быть обязательно больше tun0. Иначе клиенты OpenVPN не смогут попасть на IPv6 адреса локальные.
Для наглядности я выбрал шаг подсети 16, но при желании можно даже «1» шаг делать.
Соответственно 64+16 = 80, а 80+16 = 96.
Для ещё большей наглядности:
XXXX:XXXX:XXXX:XXXX:1:1:YYYY:YYYY — это адреса, которые должны быть назначены конкретным сайтам или сервисам на интерфейсе eno1.
XXXX:XXXX:XXXX:XXXX:1:2:YYYY:YYYY — это адреса, которые должны быть назначены конкретным сайтам или сервисам на интерфейсе eno2.
XXXX:XXXX:XXXX:XXXX:1:3:YYYY:YYYY — это адреса, которые должны быть назначены клиентам OpenVPN или использоваться как служебные адреса OpenVPN.

Для настройки сети — должна быть возможность перезагружать сервер.
IPv4 изменения подхватываются при выполнении (обязательно завернуть в screen — иначе эта команда просто уронит сеть на сервере):

/etc/init.d/networking restart

В файл "/etc/iproute2/rt_tables" добавить в конец:

100 eno1t
101 eno2t

Без этого нельзя использовать кастомные table в файле "/etc/network/interfaces".
Цифры должны быть уникальные и менее 65535.

IPv6 изменения изменяются легко без перезагрузки, но для этого нужно научиться как минимум трём командам:

ip -6 addr ...
ip -6 route ...
ip -6 neigh ...

Настройка "/etc/sysctl.conf"

# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward = 1

# Do not accept ICMP redirects (prevent MITM attacks)
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0

# Do not send ICMP redirects (we are not a router)
net.ipv4.conf.all.send_redirects = 0

# For receiving ARP replies
net.ipv4.conf.all.arp_filter = 0
net.ipv4.conf.default.arp_filter = 0

# For sending ARP
net.ipv4.conf.all.arp_announce = 0
net.ipv4.conf.default.arp_announce = 0

# Enable IPv6
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0

# IPv6 configuration
net.ipv6.conf.all.autoconf = 1
net.ipv6.conf.all.accept_ra = 0

# For OpenVPN
net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.proxy_ndp = 1

# For nginx on boot
net.ipv6.ip_nonlocal_bind = 1

Это настройки «sysctl» моего сервера. Отмечу важное.

net.ipv4.ip_forward = 1

Без этого OpenVPN не будет работать никак.

net.ipv6.ip_nonlocal_bind = 1

Любой, кто попытается сделать bind IPv6 (например nginx) сразу после того, как интерфейс подялся — получит ошибку. Что такой адрес недоступен.
Чтобы избежать такой ситуации и делается такая настройка.

net.ipv6.conf.all.forwarding = 1
net.ipv6.conf.all.proxy_ndp = 1

Без этих настроек IPv6 трафик от клиента OpenVPN не выходит в мир.

Другие настройки или не относятся к делу или я не помню зачем они.
Но на всякий случай оставляю «как есть».

Для того, чтобы изменения этого файла подхватились без перезагрузки сервера — надо выполнить команду:

sysctl -p

Более детально про «table» правила: habr.com/post/108690

============= OpenVPN =============

OpenVPN IPv4 не работает без iptables.
У меня iptables вот такие для VPN:

iptables -A INPUT -p udp -s YY.YY.YY.YY --dport 1194 -j ACCEPT
iptables -A FORWARD -i tun0 -o eno1 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j SNAT --to-source XX.XX.XX.X0
##iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j MASQUERADE
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -j DROP
iptables -A FORWARD -p udp --dport 1194 -j DROP

YY.YY.YY.YY — это мой статический IPv4 адрес локальной машины.
10.8.0.0/24 — IPv4 сеть openvpn. IPv4 адреса для клиентов openvpn.
Последовательность правил важна.

iptables -A INPUT -p udp -s YY.YY.YY.YY --dport 1194 -j ACCEPT
iptables -A FORWARD -i tun0 -o eno1 -j ACCEPT
...
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p udp --dport 1194 -j DROP
iptables -A FORWARD -p udp --dport 1194 -j DROP

Это ограничение, чтобы только я со своего статического IP мог бы воспользоваться OpenVPN.

iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j SNAT --to-source XX.XX.XX.X0
  -- или --
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eno1 -j MASQUERADE

Для пробрасывания IPv4 пакетов между клиентами OpenVPN и интернетом — нужно прописать одну из этих команд.
Для разных случаев один из вариантов не подходит.
Для моего случая подходят обе команды.
Почитав документацию я выбрал первый вариант, потому что он кушает меньше CPU.

Чтобы все настройки iptables подхватывались после reboot — надо сохранить их куда-то.

iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6

Такие имена выбраны не случайно. Их использует пакет «iptables-persistent».

apt-get install iptables-persistent

Установка основного пакета OpenVPN:

apt-get install openvpn easy-rsa

Настроим шаблон для сертификатов (подставить свои значения):

make-cadir ~/openvpn-ca
cd ~/openvpn-ca
ln -s openssl-1.0.0.cnf openssl.cnf

Отредактируем настройки шаблна сертификатов:

mcedit vars
...
# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="RU"
export KEY_PROVINCE="Krasnodar"
export KEY_CITY="Dinskaya"
export KEY_ORG="Own"
export KEY_EMAIL="admin@domain1.com"
export KEY_OU="VPN"

# X509 Subject Field
export KEY_NAME="server"
...

Создаём серверный сертификат:

cd ~/openvpn-ca
source vars
./clean-all
./build-ca
./build-key-server server
./build-dh
openvpn --genkey --secret keys/ta.key

Приготовим возможность создавать итоговые «client-name.opvn» файлы:

mkdir -p ~/client-configs/files
chmod 700 ~/client-configs/files
cp /usr/share/doc/openvpn/examples/sample-config-files/client.conf ~/client-configs/base.conf
mcedit ~/client-configs/base.conf
# Client mode
client

# Interface tunnel type
dev tun

# TCP protocol
proto tcp-client

# Address/Port of VPN server
remote XX.XX.XX.X0 1194

# Don't bind to local port/address
nobind

# Don't need to re-read keys and re-create tun at restart
persist-key
persist-tun

# Remote peer must have a signed certificate
remote-cert-tls server
ns-cert-type server

# Enable compression
comp-lzo

# Custom
ns-cert-type server
tls-auth ta.key 1
cipher DES-EDE3-CBC

Приготовим скрипт, который будет сшивать все файлы в единый opvn файл.

mcedit ~/client-configs/make_config.sh
chmod 700 ~/client-configs/make_config.sh
#!/bin/bash

# First argument: Client identifier

KEY_DIR=~/openvpn-ca/keys
OUTPUT_DIR=~/client-configs/files
BASE_CONFIG=~/client-configs/base.conf

cat ${BASE_CONFIG} 
    <(echo -e '<ca>') 
    ${KEY_DIR}/ca.crt 
    <(echo -e '</ca>n<cert>') 
    ${KEY_DIR}/${1}.crt 
    <(echo -e '</cert>n<key>') 
    ${KEY_DIR}/${1}.key 
    <(echo -e '</key>n<tls-auth>') 
    ${KEY_DIR}/ta.key 
    <(echo -e '</tls-auth>') 
    > ${OUTPUT_DIR}/${1}.ovpn

Создаём первого клиента OpenVPN:

cd ~/openvpn-ca
source vars
./build-key client-name
cd ~/client-configs
./make_config.sh client-name

Файл "~/client-configs/files/client-name.ovpn" отправляем на утройство клиенту.
Для iOS клиентов надо будет сделать трюк:
Содержимое тэга «tls-auth» должно быть без комментариев.
А также поставить «key-direction 1» сразу перед тэгом «tls-auth».

Настроим конфиг OpenVPN сервера:

cd ~/openvpn-ca/keys
cp ca.crt ca.key server.crt server.key ta.key dh2048.pem /etc/openvpn
gunzip -c /usr/share/doc/openvpn/examples/sample-config-files/server.conf.gz | tee /etc/openvpn/server.conf
mcedit /etc/openvpn/server.conf
# Listen port
port 1194

# Protocol
proto tcp-server

# IP tunnel
dev tun0
tun-ipv6
push tun-ipv6

# Master certificate
ca ca.crt

# Server certificate
cert server.crt

# Server private key
key server.key

# Diffie-Hellman parameters
dh dh2048.pem

# Allow clients to communicate with each other
client-to-client

# Client config dir
client-config-dir /etc/openvpn/ccd

# Run client-specific script on connection and disconnection
script-security 2
client-connect "/usr/bin/sudo -u root /etc/openvpn/server-clientconnect.sh"
client-disconnect "/usr/bin/sudo -u root /etc/openvpn/server-clientdisconnect.sh"

# Server mode and client subnets
server 10.8.0.0 255.255.255.0
server-ipv6 XXXX:XXXX:XXXX:XXXX:1:3::/80
topology subnet

# IPv6 routes
push "route-ipv6 XXXX:XXXX:XXXX:XXXX::/64"
push "route-ipv6 2000::/3"

# DNS (for Windows)
# These are OpenDNS
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"

# Configure all clients to redirect their default network gateway through the VPN
push "redirect-gateway def1 bypass-dhcp"
push "redirect-gateway ipv6" #For iOS

# Don't need to re-read keys and re-create tun at restart
persist-key
persist-tun

# Ping every 10s. Timeout of 120s.
keepalive 10 120

# Enable compression
comp-lzo

# User and group
user vpn
group vpn

# Log a short status
status openvpn-status.log

# Logging verbosity
##verb 4

# Custom config
tls-auth ta.key 0
cipher DES-EDE3-CBC

Это нужно для того, чтобы задать статический адрес каждому клиенту (не обязательно, но я использую):

# Client config dir
client-config-dir /etc/openvpn/ccd

Самая сложная и ключевая деталь.
К сожалению OpenVPN ещё не умеет самостоятельно настраивать IPv6 gateway для клиентов.
Приходится «вручную» пробрасывать это для каждого клиента.

# Run client-specific script on connection and disconnection
script-security 2
client-connect "/usr/bin/sudo -u root /etc/openvpn/server-clientconnect.sh"
client-disconnect "/usr/bin/sudo -u root /etc/openvpn/server-clientdisconnect.sh"

Файл "/etc/openvpn/server-clientconnect.sh":

#!/bin/sh

# Check client variables
if [ -z "$ifconfig_pool_remote_ip" ] || [ -z "$common_name" ]; then
        echo "Missing environment variable."
        exit 1
fi

# Load server variables
. /etc/openvpn/variables

ipv6=""

# Find out if there is a specific config with fixed IPv6 for this client
if [ -f "/etc/openvpn/ccd/$common_name" ]; then
        # Get fixed IPv6 from client config file
        ipv6=$(sed -nr 's/^.*ifconfig-ipv6-push[ t]+([0-9a-fA-F\:]+).*$/1/p' "/etc/openvpn/ccd/$common_name")
        echo $ipv6
fi

# Get IPv6 from IPv4
if [ -z "$ipv6" ]; then
        ipp=$(echo "$ifconfig_pool_remote_ip" | cut -d. -f4)
        if ! [ "$ipp" -ge 2 -a "$ipp" -le 254 ] 2>/dev/null; then
                echo "Invalid IPv4 part."
                exit 1
        fi
        hexipp=$(printf '%x' $ipp)
        ipv6="$prefix$hexipp"
fi

# Create proxy rule
/sbin/ip -6 neigh add proxy $ipv6 dev eno1

Файл "/etc/openvpn/server-clientdisconnect.sh":

#!/bin/sh

# Check client variables
if [ -z "$ifconfig_pool_remote_ip" ] || [ -z "$common_name" ]; then
        echo "Missing environment variable."
        exit 1
fi

# Load server variables
. /etc/openvpn/variables

ipv6=""

# Find out if there is a specific config with fixed IPv6 for this client
if [ -f "/etc/openvpn/ccd/$common_name" ]; then
        # Get fixed IPv6 from client config file
        ipv6=$(sed -nr 's/^.*ifconfig-ipv6-push[ t]+([0-9a-fA-F\:]+).*$/1/p' "/etc/openvpn/ccd/$common_name")
fi

# Get IPv6 from IPv4
if [ -z "$ipv6" ]; then
        ipp=$(echo "$ifconfig_pool_remote_ip" | cut -d. -f4)
        if ! [ "$ipp" -ge 2 -a "$ipp" -le 254 ] 2>/dev/null; then
                echo "Invalid IPv4 part."
                exit 1
        fi
        hexipp=$(printf '%x' $ipp)
        ipv6="$prefix$hexipp"
fi

# Delete proxy rule
/sbin/ip -6 neigh del proxy $ipv6 dev eno1

Оба скрипта используют файл "/etc/openvpn/variables":

# Subnet
prefix=XXXX:XXXX:XXXX:XXXX:2:
# netmask
prefixlen=112

Почему тут так написано — затрудняюсь вспомнить.
Сейчас выглядит странным netmask = 112 (тут же 96 должен быть).
И prefix странный, не соответствует сети tun0.
Но ладно, оставляю «как есть».

cipher DES-EDE3-CBC

Это на любителя — я выбрал такой способ шифрования соединения.

Более детально про настройку OpenVPN IPv4: www.digitalocean.com/community/tutorials/openvpn-ubuntu-16-04-ru
Более детально про настройку OpenVPN IPv6: techblog.synagila.com/2016/02/24/build-a-openvpn-server-on-ubuntu-to-provide-a-ipv6-tunnel-over-ipv4

============= Postfix =============

Установка основного пакета:

apt-get install postfix

При установке выбрать «internet-site».

Мой "/etc/postfix/main.cf" выглядит так:

smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
biff = no

# appending .domain is the MUA's job.
append_dot_mydomain = no

readme_directory = no

# See http://www.postfix.org/COMPATIBILITY_README.html -- default to 2 on
# fresh installs.
compatibility_level = 2

# TLS parameters
smtpd_tls_cert_file=/etc/ssl/domain1.com.2018.chained.crt
smtpd_tls_key_file=/etc/ssl/domain1.com.2018.key
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtp_bind_address = XX.XX.XX.X0
smtp_bind_address6 = XXXX:XXXX:XXXX:XXXX:1:1:1:1

smtp_tls_security_level = may
smtp_tls_ciphers = export
smtp_tls_protocols = !SSLv2, !SSLv3
smtp_tls_loglevel = 1

smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
myhostname = domain1.com
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
myorigin = domain1.com
mydestination = localhost
relayhost =
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
inet_protocols = ipv4

internal_mail_filter_classes = bounce

# Storage type
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

# SMTP-Auth settings
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions =
        permit_sasl_authenticated,
        permit_mynetworks,
        #reject_invalid_hostname,
        #reject_unknown_recipient_domain,
        reject_unauth_destination,
        reject_rbl_client sbl.spamhaus.org,
        check_policy_service unix:private/policyd-spf

smtpd_helo_restrictions =
        #reject_invalid_helo_hostname,
        #reject_non_fqdn_helo_hostname,
        reject_unknown_helo_hostname

smtpd_client_restrictions =
        permit_mynetworks,
        permit_sasl_authenticated,
        reject_non_fqdn_helo_hostname,
        permit

# SPF
policyd-spf_time_limit = 3600

# OpenDKIM
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:var/run/opendkim/opendkim.sock
non_smtpd_milters = unix:var/run/opendkim/opendkim.sock

# IP address per domain
sender_dependent_default_transport_maps = pcre:/etc/postfix/sdd_transport.pcre

Рассмотрим детали этого конфига.

smtpd_tls_cert_file=/etc/ssl/domain1.com.2018.chained.crt
smtpd_tls_key_file=/etc/ssl/domain1.com.2018.key

Только спустя 8 лет после начала моей карьеры я стал понимать как работает SSL.
Поэтому я возьму на себя смелость описать как пользоваться SSL (не отвечая на вопросы «Как это работает?» и «Почему это работает?»).
Основа современного шифрования — это создание пары ключей (две очень длинные строки символов).
Один «ключ» приватный, другой ключ «публичный». Приватный ключ храним очень старательно в секрете. Публичный ключ раздаём всем желающим.
При помощи публичного ключа можно зашифровать строку текста так, что расшифровать сможет только владелец приватного ключа.
Ну вот и вся основа технологии.
Шаг №1 — https сайты.
Браузер при обращении к сайту узнаёт от веб сервера, что сайт https и поэтому запрашивает публичный ключ.
Веб сервер отдаёт публичный ключ. Браузер используя публичный ключ зашифровывает http-request и отправляет его.
Контент http-request может прочитать только тот у кого есть приватный ключ, то есть только сервер к которому выполняется обращение.
Http-request содержит в себе как минимум URI. Поэтому если в стране пытыются ограничить доступ не ко всему сайту, а к конкретной странице — то для https сайтов это сделать невозможно.
Шаг №2 — зашифрованный ответ.
Веб сервер даёт ответ, который легко могут прочитать по дороге.
Решение предельно простое — браузер у себя локально формирует такую-же пару приватный-публичный ключ для каждого https сайта.
И вместе с запросом публичного ключа сайта отправляет свой локальный публичный ключ.
Веб сервер запоминает его и при отправке http-response шифрует этим вот публичным ключём конкретного клиента.
Теперь http-response может расшифровать только обладатель приватного ключа браузера клиента (то есть сам клиент).
Шаг №3 — установка защищённого соединения по публичному каналу.
В примере №2 есть уязвимость — ничего не мешает доброжелателям перехватить http-request и подредактировать информацию о публичном ключе.
Таким образом посредник будет пракрасно видеть весь контент отправляемых-получаемых сообщений пока не сменится канал связи.
Бороться с этим предельно просто — достаточно отправить публичный ключ браузера как сообщение зашифрованное публичным ключём веб сервера.
Веб сервер тогда первым делом отправляет ответ типа «твой публичный ключ вот такой вот» и шифрует это сообщение этим же публичным ключём.
Браузер смотрит ответ — если пришло сообщение «твой публичный ключ вот такой вот» — то это 100% гарантия, что данный канал связи безопасен.
Насколько безопасен?
Само создание такого безопасного канала связи происходит со скоростью ping*2. Например 20мс.
Злоумышленник должен или заранее иметь приватный ключ одной из сторон. Или подобрать приватный ключ за пару милисекунд.
Взлом одного современного приватнога ключа займт десятилетия на суперкомпьютере.
Шаг №4 — публичная БД публичных ключей.
Очевидно, что во всей этой истории существует возможность для злоумышленика сидящего на канале связи между клиентом и сервером.
Возможность клиенту предствится сервером, а серверу представться клиентом. И сэмулировать пару ключей в обе стороны.
Тогда злоумышленик будет видеть весь трафик и будет иметь возможность «подредактировать» трафик.
Например изменить адрес куда отправлять деньги или скопировать пароль от онлайн-банка или заблокировать «неугодный» контент.
Для борьбы с такими злоумышленниками придумали публичную БД с публичными ключами для каждого https сайта.
Каждый браузер «знает» о существовании около 200 таких БД. Это предустановлено в каждый браузер.
«Знание» подкреплено публичным ключём от каждого сертификата. То есть соединение с каждым конкретным центром сертификации подделать невозможно.

Теперь есть простое понимание как пользоваться SSL для https.
Если пошевелить мозгами — то станет понятно, как спец-службы могут в этой конструкции чего-то взломать. Но это им будет стоить чудовищных усилий.
А организациям меньше АНБ или ЦРУ — практически невозможно взломать существующий уровень защиты даже для vip.

Ещё добавлю про ssh соединения. Там никаких публичных ключей нет, как же быть. Вопрос решается двумя способами.
Вариант ssh-по-паролю:
При первом соединении ssh-клиент должен предупредить, что тут у нас новый публичный ключ от ssh-сервера.
И при дальнейших соединениях если появилось предупреждение «новый публичный ключ от ssh-сервера» — будет означать, что вас пытаются прослушать.
Или при первом соединении вас прослушивали, а теперь вы общаетесь с сервером без посредников.
Собственно из-за того, что факт прослушки легко, быстро и без усилий вскрывается — этой атакой пользуются только в особых случаях под конкретного клиента.
Вариант ssh-по-ключу:
Берём флэшку, записываем на неё приватный ключ для ssh-сервера (для этого есть термины и куча ньюансов существенных, но я пишу ликбез, а не инструкцию по применению).
Публичный ключ оставляем на машине где будет ssh-клиент и его тоже держим в секрете.
Приносим флэшку к серверу, вставляем, копируем приватный ключ, а флэшку сжигаем и развеиваем прах по ветру (или хотябы форматируем с заполнением нулями).
Вот и всё — после такой операции будет невозможно взломать такое ssh соединение. Разумеется лет за 10 на суперкомпьютере можно будет посмотреть трафик — но это отдельная история.

Прошу прощения за оффтоп.

Итак, теперь когда известна теория. Расскажу про flow создания ssl сертификата.
При помощи «openssl genrsa» мы создаём приватный ключ и «заготовок» для публичного ключа.
«заготовок» отправляем сторонней компании, которой мы платим примерно $9 за самый простой сертификат.
Через пару часов мы получаем от этой сторонней компании наш «публичный» ключ и ещё набор нескольких публичных ключей.
Зачем сторонней компании платить за оформление моего публичного ключа — вопрос отдельный, тут рассматривать не будем.

Теперь понятно в чём смысл надписи:
smtpd_tls_key_file=/etc/ssl/domain1.com.2018.key

В папке "/etc/ssl" сложены все файлы для ssl вопросов.
domain1.com — название домена.
2018 — год создания ключей.
«key» — обозначение, что файл приватный-ключ.

И смысл этого файла:
smtpd_tls_cert_file=/etc/ssl/domain1.com.2018.chained.crt
domain1.com — название домена.
2018 — год создания ключей.
chained — обозначение, что тут цепочка публичных ключей (первый — наш публичный и остальные — что пришло от компании оформившей публичный ключ).
crt — обозначение, что тут готовый сертификат (публичный ключ с пояснениями техническими).

smtp_bind_address = XX.XX.XX.X0
smtp_bind_address6 = XXXX:XXXX:XXXX:XXXX:1:1:1:1

Это установка в данном случае не используется, но написано для примера.
Потому что ошибка в данном параметре приведёт к отправке от вашего сервера спама (без вашей воли).
Потом доказывайте всем, что вы не виноваты.

recipient_delimiter = +

Возможно многие не знают, так вот это стандартный символ для ранжирования пасем, и это поддерживается большинством современных почтовых серверов.
Например если у вас есть почтовый ящик «username@gmail.com» попробуйте отправть на «username+spam@gmail.com» — посмотрите что из этого получится.

inet_protocols = ipv4

Возможно это будет сбивать с толку.
Но это не просто так. Каждый новый домен — по умолчанию только IPv4, потом включаю IPv6 для каждого в отдельности.

virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

Тут мы задаём, что вся входящая почта уходит в dovecot.
А правила для domain, mailbox, alias — смотреть в БД.

/etc/postfix/mysql-virtual-mailbox-domains.cf

user = usermail
password = mailpassword
hosts = 127.0.0.1
dbname = servermail
query = SELECT 1 FROM virtual_domains WHERE name='%s'

/etc/postfix/mysql-virtual-mailbox-maps.cf

user = usermail
password = mailpassword
hosts = 127.0.0.1
dbname = servermail
query = SELECT 1 FROM virtual_users WHERE email='%s'

/etc/postfix/mysql-virtual-alias-maps.cf

user = usermail
password = mailpassword
hosts = 127.0.0.1
dbname = servermail
query = SELECT destination FROM virtual_aliases WHERE source='%s'
# SMTP-Auth settings
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

Теперь postfix знает, что принимать почту для дальнейшей отправки можно только по авторизации с dovecot.
Мне правда не очень понятно, зачем это тут дублировать. Мы же уже указали в «virtual_transport» всё что надо.
Но postfix система очень старая — наверное это кастыли от старых времён.

smtpd_recipient_restrictions =
        ...

smtpd_helo_restrictions =
        ...

smtpd_client_restrictions =
        ...

Это настраивать для каждого почтового сервера по своему.
В моём распоряжении есть 3 почтовых сервера и эти настройки очень разные из-за разных требований к использованию.
Настраивать надо внимательно — иначе спам хлынет к вам или ещё хуже — спам хлынет от вас.

# SPF
policyd-spf_time_limit = 3600

Настройка для какого-то плагина связанного с проверкой SPF входящих писем.

# OpenDKIM
milter_default_action = accept
milter_protocol = 6
smtpd_milters = unix:var/run/opendkim/opendkim.sock
non_smtpd_milters = unix:var/run/opendkim/opendkim.sock

Настройка, что все исходящие письма мы должны снабжать DKIM подписью.

# IP address per domain
sender_dependent_default_transport_maps = pcre:/etc/postfix/sdd_transport.pcre

Это ключевая деталь в маршрутизации писем при оотправки писем от php скриптов.

Файл "/etc/postfix/sdd_transport.pcre":

/^www-domain1@domain1.com$/ domain1:
/^www-domain2@domain1.com$/ domain2:
/^www-domain3@domain1.com$/ domain3:
/@domain1.com$/             domain1:
/@domain2.com$/             domain2:
/@domain3.com$/             domain3:

Слева — регулярные выражения. Справа — метка, которой отмечается письмо.
Postfix в соответствии с меткой — учтёт ещё несколько строк конфигурации для конкретного письма.
Как именно будет переконфигурирован postfix для конкретного письма — будет указано в «master.cf».

Строки 4, 5, 6 — они главные. От имени какого домена отправляем письмо — такую метку и ставим.
Но не всегда в php скриптах в старом коде указывается поле «from». Тогда на помощь приходит имя пользователя.
Статья и так обширная — не хотелось бы отвлекаться на настройку nginx+fpm.
Кратко — мы для каждого сайта задаём своего linux-user владельца. И соответственно свой fpm-pool.
Fpm-pool использует любую версию php (это прекрасно когда на одном сервере без проблем для соседних сайтов можно использовать разную версию php и даже разный php.ini).
Так вот у конкретного linux-user «www-domain2» есть сайт domain2.com. На этом сайте есть код отправки писем без указания поля from.
Так вот даже в таком случае письма будут уходить корректно и никогда не попадут в спам.

Мой "/etc/postfix/master.cf" выглядит так:

...
smtp      inet  n       -       y       -       -       smtpd
  -o content_filter=spamassassin
...
submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
...
policyd-spf  unix  -       n       n       -       0       spawn
    user=policyd-spf argv=/usr/bin/policyd-spf

spamassassin unix -     n       n       -       -       pipe
    user=spamd argv=/usr/bin/spamc -f -e
    /usr/sbin/sendmail -oi -f ${sender} ${recipient}
...
domain1  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X1
   -o smtp_helo_name=domain1.com
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:1:1:1
   -o syslog_name=postfix-domain1

domain2  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X5
   -o smtp_helo_name=domain2.com
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:2:1:1
   -o syslog_name=postfix-domain2

domain3  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X2
   -o smtp_helo_name=domain3
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:1:5:1
   -o syslog_name=postfix-domain3

Файл приведён не полностью — он и так очень большой.
Отметил только то, что изменено.

smtp      inet  n       -       y       -       -       smtpd
  -o content_filter=spamassassin
...
spamassassin unix -     n       n       -       -       pipe
    user=spamd argv=/usr/bin/spamc -f -e
    /usr/sbin/sendmail -oi -f ${sender} ${recipient}

Это настройки связанные со spamassasin, о нём позже.

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

Разрешаем присоединяться к почтовому серверу через 587 порт.
Для этого обязательно авторизоваться.

policyd-spf  unix  -       n       n       -       0       spawn
    user=policyd-spf argv=/usr/bin/policyd-spf

Включаем проверку SPF.

apt-get install postfix-policyd-spf-python

Установим пакет для SPF проверок выше.

domain1  unix -       -       n       -       -       smtp
   -o smtp_bind_address=XX.XX.XX.X1
   -o smtp_helo_name=domain1.com
   -o inet_protocols=all
   -o smtp_bind_address6=XXXX:XXXX:XXXX:XXXX:1:1:1:1
   -o syslog_name=postfix-domain1

А это самое интересное. Это возможность отправлять письма для конкретного домена с конкретного IPv4/IPv6 адреса.
Делается это ради rDNS. rDNS — это получение какой-то строки по IP адресу.
И для почты эта возможность используется для подтверждения того, что helo точно соответствует rDNS того адреса, с которого отправили email.
Если helo не соответствует домену почты, от имени кого отправили письмо — начисляются спам очки.
Helo не соответствует rDNS — начисляется много спам очков.
Соответственно для каждого домена должен быть свой IP адрес.
Для OVH — в консольке есть возможность указывать rDNS.
Для tech.ru — через саппорт вопрос решается.
Для AWS — через саппорт вопрос решается.
«inet_protocols» и «smtp_bind_address6» — это мы включаем поддержку IPv6.
Для IPv6 тоже надо rDNS прописывать.
«syslog_name» — а это для удобства чтения логов.

Покупать сертификаты рекомендую тут: www.namecheap.com/security/ssl-certificates
Настройка связки postfix+dovecot тут: www.digitalocean.com/community/tutorials/how-to-configure-a-mail-server-using-postfix-dovecot-mysql-and-spamassassin
Настройка SPF: help.ubuntu.com/community/Postfix/SPF

============= Dovecot =============

apt-get install dovecot-imapd dovecot-pop3d dovecot-lmtpd dovecot-mysql dovecot-antispam

Настройка mysql, устанавливаем сами пакеты.

Файл "/etc/dovecot/conf.d/10-auth.conf"

disable_plaintext_auth = yes
auth_mechanisms = plain login

Авторизация только в зашифрованном виде.

Файл "/etc/dovecot/conf.d/10-mail.conf"

mail_location = maildir:/var/mail/vhosts/%d/%n

Тут укажем место хранения писем.
Я хочу, чтобы они хранились в файлах и были сгруппированы по доменам.

Файл "/etc/dovecot/conf.d/10-master.conf"

service imap-login {
  inet_listener imap {
    port = 0
  }
  inet_listener imaps {
    address = XX.XX.XX.X1, XX.XX.XX.X2, XX.XX.XX.X5, [XXXX:XXXX:XXXX:XXXX:1:1:1:1], [XXXX:XXXX:XXXX:XXXX:1:2:1:1], [XXXX:XXXX:XXXX:XXXX:1:1:5:1]
    port = 993
    ssl = yes
  }
}
service pop3-login {
  inet_listener pop3 {
    port = 0
  }
  inet_listener pop3s {
    address = XX.XX.XX.X1, XX.XX.XX.X2, XX.XX.XX.X5, [XXXX:XXXX:XXXX:XXXX:1:1:1:1], [XXXX:XXXX:XXXX:XXXX:1:2:1:1], [XXXX:XXXX:XXXX:XXXX:1:1:5:1]
    port = 995
    ssl = yes
  }
}
service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}
service imap {
}
service pop3 {
}
service auth {
  unix_listener auth-userdb {
    mode = 0600
    user = vmail
  }

  unix_listener /var/spool/postfix/private/auth {
    mode = 0666
    user = postfix
    group = postfix
  }
  user = dovecot
}
service auth-worker {
  user = vmail
}
service dict {
  unix_listener dict {
  }
}

Это главный файл настроек dovecot.
Тут мы отключаем не защищённые соединения.
И включаем защищённые соединения.

Файл "/etc/dovecot/conf.d/10-ssl.conf"

ssl = required
ssl_cert = </etc/nginx/ssl/domain1.com.2018.chained.crt
ssl_key = </etc/nginx/ssl/domain1.com.2018.key
local XX.XX.XX.X5 {
  ssl_cert = </etc/nginx/ssl/domain2.com.2018.chained.crt
  ssl_key =  </etc/nginx/ssl/domain2.com.2018.key
}

Настраиваем ssl. Указываем, что ssl — обязательно.
И сам сертификат. И важная деталь — директива «local». Указывает, при соединении к какому локальному IPv4 — какой ssl сертификат использовать.
Кстати IPv6 тут не настроен, исправлю это упущение как-нить потом.
XX.XX.XX.X5 (domain2) — сертификата нет. Для соединения клиентов нужно указывать domain1.com.
XX.XX.XX.X2 (domain3) — сертификат есть, для соединения клиентов можно указывать domain1.com или domain3.com .

Файл "/etc/dovecot/conf.d/15-lda.conf"

protocol lda {
  mail_plugins = $mail_plugins sieve
}

Это в дальнейшем нужно будет для spamassassin.

Файл "/etc/dovecot/conf.d/20-imap.conf"

protocol imap {
  mail_plugins = $mail_plugins antispam
}

Это antispam плагин. Нужен для обучения spamassasin в момент переноса в/из папки «Spam».

Файл "/etc/dovecot/conf.d/20-pop3.conf"

protocol pop3 {
}

Просто такой файл есть.

Файл "/etc/dovecot/conf.d/20-lmtp.conf"

protocol lmtp {
  mail_plugins = $mail_plugins sieve
  postmaster_address = admin@domain1.com
}

Настройка lmtp.

Файл "/etc/dovecot/conf.d/90-antispam.conf"

plugin {
  antispam_backend = pipe
  antispam_trash = Trash;trash
  antispam_spam = Junk;Spam;SPAM
  antispam_pipe_program_spam_arg = --spam
  antispam_pipe_program_notspam_arg = --ham
  antispam_pipe_program = /usr/bin/sa-learn
  antispam_pipe_program_args = --username=%Lu
}

Настройки обучения spamassasin в момент переноса в/из папки «Spam».

Файл "/etc/dovecot/conf.d/90-sieve.conf"

plugin {
  sieve = ~/.dovecot.sieve
  sieve_dir = ~/sieve
  sieve_after = /var/lib/dovecot/sieve/default.sieve
}

Файл в котором указано что делать со входящими письмами.

Файл "/var/lib/dovecot/sieve/default.sieve"

require ["fileinto", "mailbox"];

if header :contains "X-Spam-Flag" "YES" {
        fileinto :create "Spam";
}

Надо скомпилировать файл: «sievec default.sieve».

Файл "/etc/dovecot/conf.d/auth-sql.conf.ext"

passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
  driver = static
  args = uid=vmail gid=vmail home=/var/mail/vhosts/%d/%n
}

Указание sql файлов для авторизации.
А сам файл — как способ авторизации.

Файл "/etc/dovecot/dovecot-sql.conf.ext"

driver = mysql
connect = host=127.0.0.1 dbname=servermail user=usermail password=password
default_pass_scheme = SHA512-CRYPT
password_query = SELECT email as user, password FROM virtual_users WHERE email='%u';

Это соответствует аналогичным настройкам для postfix.

Файл "/etc/dovecot/dovecot.conf"

protocols = imap lmtp pop3
listen = *, ::
dict {
}
!include conf.d/*.conf
!include_try local.conf

Основной файл конфигурации.
Важно то, что мы тут указываем-добавляем протоколы.

============= SpamAssassin =============

apt-get install spamassassin spamc

Установим пакеты.

adduser spamd --disabled-login

Добавим пользователя от имени которого.

systemctl enable spamassassin.service

Включаем авто-загрузку spamassassin сервис при загрузке.

Файл "/etc/default/spamassassin":

CRON=1

Включаев автоматическое обновление правил «по умолчанию».

Файл "/etc/spamassassin/local.cf":

report_safe 0

use_bayes          1
bayes_auto_learn   1
bayes_auto_expire  1
bayes_store_module Mail::SpamAssassin::BayesStore::MySQL
bayes_sql_dsn      DBI:mysql:sa:localhost:3306
bayes_sql_username sa
bayes_sql_password password

Нужно сделать в mysql БД «sa» с пользователем «sa» с паролем «password» (заменить на что-то адекватное).
report_safe — это вместо письма будет присылаться отчёт о письме-спаме.
use_bayes — это настройки машинного обучения spamassassin.

Остальные настройки spamassassin применялись ранее по статье.

Общая настройка «spamassassin»: vorkbaard.nl/installing-a-mailserver-on-debian-8-part-6-spamfiltering-spamassassin
Про перемещение новых Спам-писем в IMAP папку «Spam»: stackoverflow.com/questions/24256008/how-to-move-spam-to-spam-folder
Про простую связку Dovecot + SpamAssassin: forum.iredmail.org/topic8169-iredmail-support-antispam-via-dovecot-and-spamassassin.html
Рекомендую к прочтению теория обучения spamassasin при движении писем в imap папках (и не рекомендую к применению): raimue.blog/2016/08/21/setting-up-dovecot-antispam-with-spamassassin

============= Обращение к сообществу =============

Ещё хотелось бы закинуть идею в сообщество про то, как повысить уровень защищённости пересылаемых писем. Раз уж я так глубоко погрузился в тему почты.
Чтобы пользователь мог бы у себя на клиенте (outlook, thunderbird, browser-plugin, ...) создать пару ключей. Публичный и приватный. Публичный — отправить в DNS. Приватный — сохранять на клиенте. Почтовые сервера бы умели применять публичный ключ для отправки конкретному адресату.
И для защиты от спама при таких письмах (да, почтовый сервер жи не сможет посмотреть контент) — надо будет ввести 3 правила:
1. Обязательная настоящая подпись DKIM, обязательный SPF, обязательный rDNS.
2. Нейронная сеть на тему обучения антиспама + БД к ней на стороне клиента.
3. Алгоритм шифрования должен быть таким, что отправляющая сторона должна потратить на шифрования в 100 раз больше мощностей CPU, чем принимающая сторона.

Кроме публичных писем — разработать стандарт письма-предложения «начать защищённую переписку». Один из пользователей (почтовый ящик) шлёт другому почтовому ящику письмо с аттачем. В письме текст-предложение начать защищённый канал связи для переписки и публичный ключ владельца почтового ящика (при этом приватный ключ на стороне клиента). Можно даже пару ключей делать специально для каждой переписки. Пользователь-получатель может принять это предложение и отправить свой публичный ключ (тоже сделанный специально для данной переписки). Далее первый пользователь отправляет служебное контрольное письмо (зашифрованное публичным ключом второго пользователя) — при получении которого второй пользователь может считать сформированный канал связи надёжным. Далее второй пользователь отправляет контрольное письмо — и тогда первый пользователь тоже может считать сформированный канал защищённым.

Для борьбы с перехватом ключей по дороге — надо в протоколе предусмотреть возможность передачи хотя-бы одного публичного ключа при помощи флэшки.

И самое главное — чтобы это всё работало (вопрос «а кто за это заплатит?»):
Ввести почтовые сертификаты стоимостью от 10$ за 3 года. Которые будут позволять отправителю указать в dns, что «мои публичные ключи находятся вон-там». И будут давать возможность начинать защищённое соединение. При этом — принимать такие соединения бесплатно.
gmail наконец монетизирует своих пользователей. За 10$ в 3 года — право создавать защищённые каналы переписки.

============= Заключение =============

Для тестирование всей статьи я собирался арендовать выделенный сервер на месяц и купить домен с ssl сертификатом.
Но жизненные обстоятельства сложились так этот вопрос затянулся на 2 месяца.
И вот когда появилось снова свободное время — решил публиковать статью как есть, а не рисковать тем, что публикация затянется ещё на год.

Если будет достаточно много вопросов типа «а вот тут не достаточно подробно описано» — тогда наверное найдутся силы таки взять выделенный сервер с новым доменом и новым SSL сертификатом и ещё подробнее описать и главное — выявить все упущенные важные детали.

Также хотелось бы получить отзывы на тему идеи про почтовые сертификаты. Если идея понравится — постараюсь найти силы написать черновик для rfc.

При копировании больших кусков статьи — указывать ссылку на эту статью.
При переводе на любой другой язык — указывать ссылку на эту статью.
На английский язык я сам постараюсь перевести и оставлю перекрёстные ссылки.

Автор: Александр

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js