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

Дана стаття про те, як налаштувати сучасний поштовий сервер.
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 обліковий запис, на який хочеться прив'язати поштову скриньку `[захищено електронною поштою]` (отримання пошти та надсилання пошти прямо з gmail інтерфейсу).
Повинна бути поштова скринька `[захищено електронною поштою]`, копію пошти з якого я хочу бачити у себе в gmail. І рідко мати можливість відправити чогось від імені `[захищено електронною поштою]через web-інтерфейс.

Повинна бути поштова скринька `[захищено електронною поштою]`яким буде користуватися Іванов зі свого 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" - сенс цих 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="[email protected]"
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}/.crt 
    <(echo -e '</cert>n<key>') 
    ${KEY_DIR}/.key 
    <(echo -e '</key>n<tls-auth>') 
    ${KEY_DIR}/ta.key 
    <(echo -e '</tls-auth>') 
    > ${OUTPUT_DIR}/.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

На думку хабровчан цей блок містить "дезінформацію та невірні тези".Лише через 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 = +

Можливо багато хто не знає, так це стандартний символ для ранжування пасем, і це підтримується більшістю сучасних поштових серверів.

Наприклад, якщо у вас є поштова скринька «[захищено електронною поштою]» спробуйте надіслати на «[захищено електронною поштою]» - Подивіться що з цього вийде.

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, або — дивитися в БД.

/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":

/^[email protected]$/ domain1:
/^[email protected]$/ domain2:
/^[email protected]$/ 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. На цьому сайті є код надсилання листів без вказівки поля від.

Так ось навіть у такому разі листи будуть йти коректно і ніколи не потраплять до спаму.

Мій "/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" - а це для зручності читання логів.

Купувати сертифікати рекомендую тут.

Налаштування зв'язки 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 обов'язково.
І сам сертифікат. І важлива деталь - директива "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 = [email protected]
}

Налаштування 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 правила:

  1. Обов'язковий цей підпис DKIM, обов'язковий SPF, обов'язковий rDNS.
  2. Нейронна мережа на тему навчання антиспаму + БД до неї за клієнта.
  3. Алгоритм шифрування повинен бути таким, що сторона, що відправляє, повинна витратити на шифрування в 100 разів більше потужностей CPU, ніж сторона, що приймає.

Окрім публічних листів — розробити стандарт листа-пропозиції «почати захищене листування». Один з користувачів (поштова скринька) надсилає іншій поштовій скриньці лист з атачем. У листі текст-пропозиція розпочати захищений канал зв'язку для листування та публічний ключ власника поштової скриньки (при цьому приватний ключ на стороні клієнта).

Можна навіть кілька ключів робити спеціально для кожного листування. Користувач-одержувач може прийняти цю пропозицію та надіслати свій публічний ключ (теж зроблений спеціально для цього листування). Далі перший користувач надсилає службовий контрольний лист (зашифрований публічним ключем другого користувача) - при отриманні якого другий користувач може вважати сформований канал зв'язку надійним. Далі другий користувач надсилає контрольний лист - і тоді перший користувач теж може вважати сформований канал захищеним.

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

І найголовніше – щоб це все працювало (питання «а хто за це заплатить?»):
Ввести поштові сертифікати вартістю від 10 $ за 3 роки. Які будуть дозволяти відправнику вказати в dns, що «мої публічні ключі знаходяться там». І даватимуть можливість починати захищене з'єднання. При цьому приймати такі з'єднання безкоштовно.
gmail нарешті монетизує своїх користувачів. За 10 $ на 3 роки - право створювати захищені канали листування.

============= Висновок =============

Для тестування всієї статті я збирався орендувати виділений сервер на місяць та купити домен із ssl сертифікатом.

Але життєві обставини склалися, так це питання затягнулося на 2 місяці.
І ось, коли з'явився знову вільний час — вирішив публікувати статтю як є, а не ризикувати тим, що публікація затягнеться ще на рік.

Якщо буде досить багато питань типу «а ось тут мало докладно описано» - тоді напевно знайдуться сили взяти виділений сервер з новим доменом і новим SSL сертифікатом і ще докладніше описати і головне - виявити всі втрачені важливі деталі.

Також хотілося б отримати відгуки на тему ідеї про поштові сертифікати. Якщо ідея сподобається - постараюсь знайти сили написати чернетку для rfc.

При копіюванні великих шматків статті – вказувати посилання на цю статтю.
При перекладі будь-якою іншою мовою — вказувати посилання на цю статтю.
Англійською мовою я сам постараюся перекласти і залишу перехресні посилання.


Джерело: habr.com

Додати коментар або відгук