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.
Более детально про настройку OpenVPN IPv6.
============= 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
Итак, теперь когда известна теория. Расскажу про flow создания ssl сертификата.По мнению хабровчан данный блок содержит `дезинформацию и неверные тезисы`.Только спустя 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 на суперкомпьютере можно будет посмотреть трафик — но это отдельная история.
Прошу прощения за оффтоп.
При помощи «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:
Слева — регулярные выражения. Справа — метка, которой отмечается письмо.Мой "/etc/postfix/master.cf" выглядит так:
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.
Так вот даже в таком случае письма будут уходить корректно и никогда не попадут в спам.
...
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» — а это для удобства чтения логов.
Настройка связки postfix+dovecot тут.
Настройка 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 — обязательно.Файл "/etc/dovecot/conf.d/15-lda.conf"
И сам сертификат. И важная деталь — директива «local». Указывает, при соединении к какому локальному IPv4 — какой ssl сертификат использовать.
Кстати IPv6 тут не настроен, исправлю это упущение как-нить потом.
XX.XX.XX.X5 (domain2) — сертификата нет. Для соединения клиентов нужно указывать domain1.com.
XX.XX.XX.X2 (domain3) — сертификат есть, для соединения клиентов можно указывать domain1.com или domain3.com .
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».
Про перемещение новых Спам-писем в IMAP папку «Spam».
Про простую связку Dovecot + SpamAssassin.
Рекомендую к прочтению теория обучения spamassasin при движении писем в imap папках (и не рекомендую к применению).
============= Обращение к сообществу =============
Ещё хотелось бы закинуть идею в сообщество про то, как повысить уровень защищённости пересылаемых писем. Раз уж я так глубоко погрузился в тему почты.
Чтобы пользователь мог бы у себя на клиенте (outlook, thunderbird, browser-plugin, ...) создать пару ключей. Публичный и приватный. Публичный — отправить в DNS. Приватный — сохранять на клиенте. Почтовые сервера бы умели применять публичный ключ для отправки конкретному адресату.
И для защиты от спама при таких письмах (да, почтовый сервер жи не сможет посмотреть контент) — надо будет ввести 3 правила:
- Обязательная настоящая подпись DKIM, обязательный SPF, обязательный rDNS.
- Нейронная сеть на тему обучения антиспама + БД к ней на стороне клиента.
- Алгоритм шифрования должен быть таким, что отправляющая сторона должна потратить на шифрования в 100 раз больше мощностей CPU, чем принимающая сторона.
Кроме публичных писем — разработать стандарт письма-предложения «начать защищённую переписку». Один из пользователей (почтовый ящик) шлёт другому почтовому ящику письмо с аттачем. В письме текст-предложение начать защищённый канал связи для переписки и публичный ключ владельца почтового ящика (при этом приватный ключ на стороне клиента).
Можно даже пару ключей делать специально для каждой переписки. Пользователь-получатель может принять это предложение и отправить свой публичный ключ (тоже сделанный специально для данной переписки). Далее первый пользователь отправляет служебное контрольное письмо (зашифрованное публичным ключом второго пользователя) — при получении которого второй пользователь может считать сформированный канал связи надёжным. Далее второй пользователь отправляет контрольное письмо — и тогда первый пользователь тоже может считать сформированный канал защищённым.
Для борьбы с перехватом ключей по дороге — надо в протоколе предусмотреть возможность передачи хотя-бы одного публичного ключа при помощи флэшки.
И самое главное — чтобы это всё работало (вопрос «а кто за это заплатит?»):
Ввести почтовые сертификаты стоимостью от 10$ за 3 года. Которые будут позволять отправителю указать в dns, что «мои публичные ключи находятся вон-там». И будут давать возможность начинать защищённое соединение. При этом — принимать такие соединения бесплатно.
gmail наконец монетизирует своих пользователей. За 10$ в 3 года — право создавать защищённые каналы переписки.
============= Заключение =============
Для тестирования всей статьи я собирался арендовать выделенный сервер на месяц и купить домен с ssl сертификатом.
Но жизненные обстоятельства сложились так этот вопрос затянулся на 2 месяца.
И вот когда появилось снова свободное время — решил публиковать статью как есть, а не рисковать тем, что публикация затянется ещё на год.
Если будет достаточно много вопросов типа «а вот тут не достаточно подробно описано» — тогда наверное найдутся силы таки взять выделенный сервер с новым доменом и новым SSL сертификатом и ещё подробнее описать и главное — выявить все упущенные важные детали.
Также хотелось бы получить отзывы на тему идеи про почтовые сертификаты. Если идея понравится — постараюсь найти силы написать черновик для rfc.
При копировании больших кусков статьи — указывать ссылку на эту статью.
При переводе на любой другой язык — указывать ссылку на эту статью.
На английский язык я сам постараюсь перевести и оставлю перекрёстные ссылки.
Комментарии (31)
in_heb
07.04.2019 01:21+4Почитайте RFC 5737, 3849. Не очень удобно читать howto, когда ip-адреса имеют вид xx.xx.xx.xx
Зря вы добавили openvpn в статью. К почте он отношения не имеет. В итоге получалась какая-то каша из настройки маршрутизации, vpn-а и почты. Хотя бы пометьте какие настройки не нужны, если не нужен vpn.podivilov
07.04.2019 16:50Тоже статья показалось несколько странной. Если уж и рассказывать про настройку почтового сервера, то про самый распространённый кейс, подходящий многим. А вот что до слов автора «В статье отсутствуют попытки пояснить каждую деталь [...]» — очень жаль. В случае с почтой обычно никакие подробности опускать нельзя, потому как дело это весьма хитрое и замысловатое.
anlide Автор
07.04.2019 18:40Это конкретный случай, когда в системе есть несколько доменов и несколько физических интерфейсов. Поэтому про маршрутизацию было важно рассказать.
Почему я решил включить openvpn? Потому что в РФ нет возможности получить IPv6 без проблем на конечную машину. А как тестировать IPv6 без openvpn в такой ситуации? Никак. Потому и включил его в статью.
Спасибо за ссылки на RFC.in_heb
07.04.2019 19:28+1Просто писать про vpn в статье про почту это жуткий оверкилл.
В РФ нативно даёт Эртелеком и МТС (на мобильной сети), т.е. получить нативный ipv6 не так уж и сложно.
Ну ладно, допустим у вашего isp его нет. Но есть же туннельные брокеры https://en.m.wikipedia.org/wiki/List_of_IPv6_tunnel_brokers (для тестов это более чем достаточно)
Допустим, по какой-то причине и это не вариант. Тогда берёте самую дешёвую vps под это дело (рекомендую использовать lowendbox/lowendtalk для поиска) и тестирует с неё, а то у вас даже тест не совсем честный, по сути внутри хоста тестируете часть вещей.
wtpltd
07.04.2019 02:06Таки, если необходимо, выделенный сервер предоставить готов. И выделенный домен. Что касается сертификатов — как на счет летсенкриптов?
И, если уж заморачиваться с собственным почтовиком, так это или здоровая параноя или корпоративные фишки, которые никто предоставить не может.
Сможете запилить и я буду первым подписчиком вашего стартапа :)anlide Автор
07.04.2019 18:35Финансово легко сам потяну. Времени резко меньше стало свободного в последнее время.
Собственный почтовый сервер имеет смысл ставить в корпоративных целях. Или когда вам катастрофически важна гарантия доставки писем и/или максимальный антиспам рейтинг.in_heb
07.04.2019 19:32Это всё до первого ддос и прочих атак, а потом вы начинаете прятаться за антиддос сервис (что вносит огромные коррективы ко всей схеме) или уходите на публичные провайдеры типа Microsoft (office365)
KAndy
07.04.2019 05:12-4Вот меня удивляет, что люди в 2019 году, по-прежнему, запускают сервисы не в docker/kuberntes
mrobespierre
07.04.2019 06:09+4Все люди разные. Некоторым нужны безопасность, стабильность, надёжность и производительность, которых Docker с его тысячами открытых issues предоставить не может. А ещё некторые понимают, что Docker — средство доставки приложений. Если ваш стартап выкатывает новую версию сервиса 2 раза в неделю, Docker незаменим. Если вы поднимаете почтовый сервак, который будет работать годами Docker вообще ни к чему.
immaculate
07.04.2019 12:26+1Помимо всего, что уже написали, работать с Docker банально менее удобно. И непонятно зачем добавлять лишние сущности и уровни абстракции в данном конкретном случае. Кроме лишней возни это ничего не добавит.
Ну и бонусом идет, например, невозможность безопасной настройки LetsEncrypt в докере. Все решения, которые я видел, реализованы с открытием лазейки позволяющей выходить за пределы контейнера.
rrpv
07.04.2019 05:55+2А это самое интересное. Это возможность отправлять письма для конкретного домена с конкретного IPv4/IPv6 адреса.
…
Если helo не соответствует домену почты, от имени кого отправили письмо — начисляются спам очки.Сколько можно распространять по интернету эту глупость?
Кто-то один раз где-то это написал, и теперь каждый натыкающийся на эту "кладезь мудрости" старается внедрить её у себя и рассказать миру об этом достижении.
HELO не обязано соответствовать домену адреса отправителя, и никакие известные мне почтовые антиспамы за это штрафные баллы не начисляют.
Соответственно для каждого домена должен быть свой IP адрес.
Такого требования также нет.
anlide Автор
07.04.2019 18:32Почтовые сервера на outlook.com всегда делают bounce письмам отправленным с сервера, у которого неправильный HELO.
spamassassin, который установлен у многих почтовых серверов, по умолчанию даёт 1.6 очков спама на неправильный HELO.
Так-то да, если вам не важно попадёт письмо в спам или нет — то на HELO можно не обращать внимание.VitalKoshalew
07.04.2019 21:02Вы путаете «неправильный» адрес в HELO (который не соответствует прямому и обратному DNS) и то, что у автора статьи — «helo не соответствует домену почты, от имени кого отправили письмо». Последнее — выдумка, потому что тот же outlook.com шлёт почту от имени тысяч доменов, нет и не может быть такого ограничения. Соответствие записям SPF/DMARC домена-отправителя могут проверяться, а сравнивать домен одного из почтовых серверов в цепочке почему-то с адресом FROM в письме, я не знаю, кому в голову может прийти такая идея, видимо это urban legend.
konchok
07.04.2019 21:15В идеале HELLO должен точно соответствовать MX записи, reverse DNS этого IP и имени хоста в SSL сертификате — спам-фильтры такому очень радуются. Какой-то аутлук это не показатель ничего вообще.
fluffybear
07.04.2019 08:21Вместо связки OpenDKIM + SpamAssasin не смотрели в сторону Rspamd? Шустрый, работает «из коробки» с минимальными настройками.
neol
07.04.2019 09:32Также хотелось бы получить отзывы на тему идеи про почтовые сертификаты. Если идея понравится — постараюсь найти силы написать черновик для rfc.
Его уже написали, одобрили и чёрт знает сколько лет успешно используют: tools.ietf.org/html/rfc4880anlide Автор
07.04.2019 18:45Я в курсе про PGP. Но пользоваться им неудобно, уж простите.
Нужен какой-то новый стандарт, при помощи которого в один клик пользователи могли бы установить защищённое соединение. На уровне клиентов. И чтобы это было бы массово распространено среди всех существующих почтовых клиентов.darken99
07.04.2019 19:16Это кроме параноиков или людей занимающихся не совсем законной деятельностью нафиг никому не надо. А кому надо тот и PGP освоит.
neol
07.04.2019 19:18
PGP за почти 3 десятка лет существования не получил распространения среди всех существующих почтовых клиентов ( хотя на мой взгляд в Evolution или Thunderbird пользоваться им достаточно легко ), а вы надеетесь вот так просто взять и договориться с Microsoft, Google, IBM, Apple и кучей компаний поменьше? Да вам сказки надо писать, а не RFC.
pae174
07.04.2019 11:06+1> Только спустя 8 лет после начала моей карьеры я стал понимать как работает SSL.
>…
> Веб сервер отдаёт публичный ключ. Браузер используя публичный ключ зашифровывает
> http-request и отправляет его.
8 лет оказалось недостаточно.1c80
07.04.2019 11:24Можете ли пояснить, что не так?
pae174
07.04.2019 14:01+1Всё не так. Эта ваша желтая секция про HTTPS содержит набор ереси. Там просто через сстрочку можно писать ответ «это не так, читайте вики.»
> Браузер используя публичный ключ зашифровывает http-request и отправляет его.
Это не так, читайте вики: TLS.
> если в стране пытаются ограничить доступ не ко всему сайту,
> а к конкретной странице — то для https сайтов это сделать невозможно.
Очень даже возможно. Для этого государству надо либо вступить в сговор с CA (wiki: StartCom), либо самому стать CA ( habr.com/ru/post/272207 ), либо заюзать что-то типа Carnivore или NarusInsight (но это не точно, агентство которого нет всё отрицает а вики врет). Хотя, если вы король какой-нибудь банановой республики или царек в Рога и Копыта Интернешнл, то вы можете так не заморачиваться а просто связаться с Symantec Solutions и они вам помогут с перехватом HTTPS трафика всех этих ваших холопов.
> браузер у себя локально формирует такую-же пару приватный-публичный ключ
> для каждого https сайта
Не фомирует браузер никакую такую пару ключей.
> И вместе с запросом публичного ключа сайта отправляет свой локальный публичный ключ.
wiki: TLS. Ну или вы неправильно поняли аутентификацию по сертификатам, которая к тому же на публичных сайтах всё равно не применяется.
> http-response шифрует этим вот публичным ключём конкретного клиента.
wiki: TLS, сеансовый ключ там используется на самом деле а не публичный.
Вместо ожидаемого упоминания Диффи-Хеллмана написана какая-то ерунда.
> Само создание такого безопасного канала связи происходит со скоростью ping*2
Два пинга будет только если не учитывать TCP handshake (+1 пинг), не смотреть в CRL (много пингов), не дергать никого по OSCP (много-много пингов). Ну то есть если тупо не проверять совсем ничего и всегда слепо доверять всем без разбору — тогда да, парой-тройкой пингов можно обойтись. Хотя OCSP stapling устраняет эти вот многочисленные лишние пинги. Но его используют только 30% хостов примерно, а у вас он так и вообще не упомянут.
> Для борьбы с такими злоумышленниками придумали публичную БД
> с публичными ключами для каждого https сайта.
Вы, наверное, CA имели в виду. Там у них нет никакой публичной базы публичных ключей. Если CRL — публичные списки отозванных сертификатов — то есть таких, которые раньше можно было использовать а теперь нельзя. Есть OSCP, но это запрос-ответ а не скачивание базы чего-либо.
> Ещё добавлю про ssh соединения. Там никаких публичных ключей нет.
Да ну?
> При первом соединении ssh-клиент должен предупредить, что тут у нас новый публичный ключ.
Вы же только что написали, что никаких публичных ключей нет.
> Через пару часов мы получаем от этой сторонней компании наш «публичный» ключ и ещё набор нескольких публичных ключей.
Сертификат вы оттуда получаете подписанный а вовсе не ключ. Это разные вещи.
> chained — обозначение, что тут цепочка публичных ключей
Цепочка сертификатов. Не бывает никаких цепочек ключей в природе.
1c80
07.04.2019 14:09ну блин..., спасибо за информацию.
оказалось что всё еще на порядок сложнее и как правильно настроить это добро даже и приблизительно не понятно теперь, как же правильно то сделать?
anlide Автор
07.04.2019 18:18Спасибо за развёрнутый комментарий. Я хотел донести идеи, очень простым языком. Даже упуская важные детали.
rrpv
08.04.2019 07:13+1Ваша статья требует полной переработки, с учетом вышеизложенных в комментариях замечаний. Несмотря на наличие этих замечаний, никаких изменений в содержимое статьи Вами не внесено, и она продолжает нести в массы дезинформацию и неверные тезисы.
Я бы попросил Вас скрыть её в черновики до момента переработки содержимого.
Вы уже получили много отзывов и Вам есть над чем подумать.
asoloviov
07.04.2019 14:10Если уж включать spam паранойю, то я бы добавил еще:
- поддержку DMARC и ARC
- дополнительные и/или свои блеклисты (это отсылка к строке
reject_rbl_client sbl.spamhaus.org,
) - например
fail2ban
с реакцией на логи postfix о несовпадении hostname<->rDNS, невалидные сертификаты, оборванные входящие TLS/SSL сессии
Ну и плюсую за OpenPGP aka RFC4880
konchok
07.04.2019 19:06Вместо костыльного spamassassin имеется приятный и технологичный rspamd. Он и DKIM умеет из коробки. Непонятно зачем тут нужнен OpenVPN лучше бы рассмотрели веб интерфейс для почты.
Ну и напоследок — есть же www.iredmail.org, вся связка уже готовая и настроеная.
neenik
Посмотрите mailcow.