Данная статья о том как настроить современный почтовый сервер.
Postfix + Dovecot. SPF + DKIM + rDNS. С IPv6.
С шифрованием TLS. С поддержкой нескольких доменов — часть с настоящим 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.
Для настройки интерфейсов надо прописать вот такое в "/etc/network/interfaces".
Данные настройки можно применять на любом сервере в 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.
Этой командой мы задаём, что любой непонятный трафик, который попал под любое rule у которого отмечено «table eno1t» -> направить в интерфейс eno1.
Этой командой мы задаём, что любой трафик по инициативе сервера направить в интерфейс eno1.
Этой командой мы задаём сами правила маркировки трафика.
Этот блок задаёт второй IPv4 для интерфейса eno1.
Этой командой мы задаём route от клиентов OpenVPN до локальных IPv4 кроме XX.XX.XX.X0.
Почему этой команды достаточно для всех IPv4 — я до сих пор не понимаю.
Это мы задаём адрес для самого интерфейса. Сервер его будет использовать как «исходящий» адрес. Больше никак использоваться не будет.
Почему указано ":1:1::" так сложно? Чтобы OpenVPN работало правильно и только для этого. Об этом подробнее позже.
На тему gateway — так работает и ладно. Но по правильному — сюда надо указать IPv6 свича к которому подсоединён сервер.
Однако почему-то IPv6 перестаёт работать, если я так делаю. Наверное это заморочки tech.ru какие-то.
Это добавление IPv6 адреса на интерфейс. Если надо сотню адресов — значит сотню строк в этом файле.
Для настройки сети — должна быть возможность перезагружать сервер.
IPv4 изменения подхватываются при выполнении (обязательно завернуть в screen — иначе эта команда просто уронит сеть на сервере):
В файл "/etc/iproute2/rt_tables" добавить в конец:
Без этого нельзя использовать кастомные table в файле "/etc/network/interfaces".
Цифры должны быть уникальные и менее 65535.
IPv6 изменения изменяются легко без перезагрузки, но для этого нужно научиться как минимум трём командам:
Настройка "/etc/sysctl.conf"
Это настройки «sysctl» моего сервера. Отмечу важное.
Без этого OpenVPN не будет работать никак.
Любой, кто попытается сделать bind IPv6 (например nginx) сразу после того, как интерфейс подялся — получит ошибку. Что такой адрес недоступен.
Чтобы избежать такой ситуации и делается такая настройка.
Без этих настроек IPv6 трафик от клиента OpenVPN не выходит в мир.
Другие настройки или не относятся к делу или я не помню зачем они.
Но на всякий случай оставляю «как есть».
Для того, чтобы изменения этого файла подхватились без перезагрузки сервера — надо выполнить команду:
Более детально про «table» правила: habr.com/post/108690
OpenVPN IPv4 не работает без iptables.
У меня iptables вот такие для VPN:
YY.YY.YY.YY — это мой статический IPv4 адрес локальной машины.
10.8.0.0/24 — IPv4 сеть openvpn. IPv4 адреса для клиентов openvpn.
Последовательность правил важна.
Это ограничение, чтобы только я со своего статического IP мог бы воспользоваться OpenVPN.
Для пробрасывания IPv4 пакетов между клиентами OpenVPN и интернетом — нужно прописать одну из этих команд.
Для разных случаев один из вариантов не подходит.
Для моего случая подходят обе команды.
Почитав документацию я выбрал первый вариант, потому что он кушает меньше CPU.
Чтобы все настройки iptables подхватывались после reboot — надо сохранить их куда-то.
Такие имена выбраны не случайно. Их использует пакет «iptables-persistent».
Установка основного пакета OpenVPN:
Настроим шаблон для сертификатов (подставить свои значения):
Отредактируем настройки шаблона сертификатов:
Создаём серверный сертификат:
Приготовим возможность создавать итоговые «client-name.opvn» файлы:
Приготовим скрипт, который будет сшивать все файлы в единый opvn файл.
Создаём первого клиента OpenVPN:
Файл "~/client-configs/files/client-name.ovpn" отправляем на утройство клиенту.
Для iOS клиентов надо будет сделать трюк:
Содержимое тэга «tls-auth» должно быть без комментариев.
А также поставить «key-direction 1» сразу перед тэгом «tls-auth».
Настроим конфиг OpenVPN сервера:
Это нужно для того, чтобы задать статический адрес каждому клиенту (не обязательно, но я использую):
Самая сложная и ключевая деталь.
К сожалению OpenVPN ещё не умеет самостоятельно настраивать IPv6 gateway для клиентов.
Приходится «вручную» пробрасывать это для каждого клиента.
Файл "/etc/openvpn/server-clientconnect.sh":
Файл "/etc/openvpn/server-clientdisconnect.sh":
Оба скрипта используют файл "/etc/openvpn/variables":
Почему тут так написано — затрудняюсь вспомнить.
Сейчас выглядит странным netmask = 112 (тут же 96 должен быть).
И prefix странный, не соответствует сети tun0.
Но ладно, оставляю «как есть».
Это на любителя — я выбрал такой способ шифрования соединения.
Более детально про настройку OpenVPN IPv4.
Более детально про настройку OpenVPN IPv6.
Установка основного пакета:
При установке выбрать «internet-site».
Мой "/etc/postfix/main.cf" выглядит так:
Рассмотрим детали этого конфига.
При помощи «openssl genrsa» мы создаём приватный ключ и «заготовок» для публичного ключа.
«заготовок» отправляем сторонней компании, которой мы платим примерно $9 за самый простой сертификат.
Через пару часов мы получаем от этой сторонней компании наш «публичный» ключ и ещё набор нескольких публичных ключей.
Зачем сторонней компании платить за оформление моего публичного ключа — вопрос отдельный, тут рассматривать не будем.
Теперь понятно в чём смысл надписи:
В папке "/etc/ssl" сложены все файлы для ssl вопросов.
domain1.com — название домена.
2018 — год создания ключей.
«key» — обозначение, что файл приватный-ключ.
И смысл этого файла:
smtpd_tls_cert_file=/etc/ssl/domain1.com.2018.chained.crt
domain1.com — название домена.
2018 — год создания ключей.
chained — обозначение, что тут цепочка публичных ключей (первый — наш публичный и остальные — что пришло от компании оформившей публичный ключ).
crt — обозначение, что тут готовый сертификат (публичный ключ с пояснениями техническими).
Это установка в данном случае не используется, но написано для примера.
Потому что ошибка в данном параметре приведёт к отправке от вашего сервера спама (без вашей воли).
Потом доказывайте всем, что вы не виноваты.
Возможно многие не знают, так вот это стандартный символ для ранжирования писем, и это поддерживается большинством современных почтовых серверов.
Например если у вас есть почтовый ящик «username@gmail.com» попробуйте отправть на «username+spam@gmail.com» — посмотрите что из этого получится.
Возможно это будет сбивать с толку.
Но это не просто так. Каждый новый домен — по умолчанию только IPv4, потом включаю IPv6 для каждого в отдельности.
Тут мы задаём, что вся входящая почта уходит в dovecot.
А правила для domain, mailbox, alias — смотреть в БД.
/etc/postfix/mysql-virtual-mailbox-domains.cf
/etc/postfix/mysql-virtual-mailbox-maps.cf
/etc/postfix/mysql-virtual-alias-maps.cf
Теперь postfix знает, что принимать почту для дальнейшей отправки можно только по авторизации с dovecot.
Мне правда не очень понятно, зачем это тут дублировать. Мы же уже указали в «virtual_transport» всё что надо.
Но postfix система очень старая — наверное это костыли от старых времён.
Это настраивать для каждого почтового сервера по своему.
В моём распоряжении есть 3 почтовых сервера и эти настройки очень разные из-за разных требований к использованию.
Настраивать надо внимательно — иначе спам хлынет к вам или ещё хуже — спам хлынет от вас.
Настройка для какого-то плагина связанного с проверкой SPF входящих писем.
Настройка, что все исходящие письма мы должны снабжать DKIM подписью.
Это ключевая деталь в маршрутизации писем при оотправки писем от php скриптов.
Файл "/etc/postfix/sdd_transport.pcre":
Файл приведён не полностью — он и так очень большой.
Отметил только то, что изменено.
Это настройки связанные со spamassasin, о нём позже.
Разрешаем присоединяться к почтовому серверу через 587 порт.
Для этого обязательно авторизоваться.
Включаем проверку SPF.
Установим пакет для SPF проверок выше.
Настройка связки postfix+dovecot тут.
Настройка SPF.
Настройка mysql, устанавливаем сами пакеты.
Файл "/etc/dovecot/conf.d/10-auth.conf"
Авторизация только в зашифрованном виде.
Файл "/etc/dovecot/conf.d/10-mail.conf"
Тут укажем место хранения писем.
Я хочу, чтобы они хранились в файлах и были сгруппированы по доменам.
Файл "/etc/dovecot/conf.d/10-master.conf"
Это главный файл настроек dovecot.
Тут мы отключаем не защищённые соединения.
И включаем защищённые соединения.
Файл "/etc/dovecot/conf.d/10-ssl.conf"
Это в дальнейшем нужно будет для spamassassin.
Файл "/etc/dovecot/conf.d/20-imap.conf"
Это antispam плагин. Нужен для обучения spamassasin в момент переноса в/из папки «Spam».
Файл "/etc/dovecot/conf.d/20-pop3.conf"
Просто такой файл есть.
Файл "/etc/dovecot/conf.d/20-lmtp.conf"
Настройка lmtp.
Файл "/etc/dovecot/conf.d/90-antispam.conf"
Настройки обучения spamassasin в момент переноса в/из папки «Spam».
Файл "/etc/dovecot/conf.d/90-sieve.conf"
Файл в котором указано что делать со входящими письмами.
Файл "/var/lib/dovecot/sieve/default.sieve"
Надо скомпилировать файл: «sievec default.sieve».
Файл "/etc/dovecot/conf.d/auth-sql.conf.ext"
Указание sql файлов для авторизации.
А сам файл — как способ авторизации.
Файл "/etc/dovecot/dovecot-sql.conf.ext"
Это соответствует аналогичным настройкам для postfix.
Файл "/etc/dovecot/dovecot.conf"
Основной файл конфигурации.
Важно то, что мы тут указываем-добавляем протоколы.
Установим пакеты.
Добавим пользователя от имени которого.
Включаем авто-загрузку spamassassin сервис при загрузке.
Файл "/etc/default/spamassassin":
Включаев автоматическое обновление правил «по умолчанию».
Файл "/etc/spamassassin/local.cf":
Нужно сделать в mysql БД «sa» с пользователем «sa» с паролем «password» (заменить на что-то адекватное).
report_safe — это вместо письма будет присылаться отчёт о письме-спаме.
use_bayes — это настройки машинного обучения spamassassin.
Остальные настройки spamassassin применялись ранее по статье.
Общая настройка «spamassassin».
Про перемещение новых Спам-писем в IMAP папку «Spam».
Про простую связку Dovecot + SpamAssassin.
Рекомендую к прочтению теория обучения spamassasin при движении писем в imap папках (и не рекомендую к применению).
Ещё хотелось бы закинуть идею в сообщество про то, как повысить уровень защищённости пересылаемых писем. Раз уж я так глубоко погрузился в тему почты.
Чтобы пользователь мог бы у себя на клиенте (outlook, thunderbird, browser-plugin, ...) создать пару ключей. Публичный и приватный. Публичный — отправить в DNS. Приватный — сохранять на клиенте. Почтовые сервера бы умели применять публичный ключ для отправки конкретному адресату.
И для защиты от спама при таких письмах (да, почтовый сервер жи не сможет посмотреть контент) — надо будет ввести 3 правила:
Кроме публичных писем — разработать стандарт письма-предложения «начать защищённую переписку». Один из пользователей (почтовый ящик) шлёт другому почтовому ящику письмо с аттачем. В письме текст-предложение начать защищённый канал связи для переписки и публичный ключ владельца почтового ящика (при этом приватный ключ на стороне клиента).
Можно даже пару ключей делать специально для каждой переписки. Пользователь-получатель может принять это предложение и отправить свой публичный ключ (тоже сделанный специально для данной переписки). Далее первый пользователь отправляет служебное контрольное письмо (зашифрованное публичным ключом второго пользователя) — при получении которого второй пользователь может считать сформированный канал связи надёжным. Далее второй пользователь отправляет контрольное письмо — и тогда первый пользователь тоже может считать сформированный канал защищённым.
Для борьбы с перехватом ключей по дороге — надо в протоколе предусмотреть возможность передачи хотя-бы одного публичного ключа при помощи флэшки.
И самое главное — чтобы это всё работало (вопрос «а кто за это заплатит?»):
Ввести почтовые сертификаты стоимостью от 10$ за 3 года. Которые будут позволять отправителю указать в dns, что «мои публичные ключи находятся вон-там». И будут давать возможность начинать защищённое соединение. При этом — принимать такие соединения бесплатно.
gmail наконец монетизирует своих пользователей. За 10$ в 3 года — право создавать защищённые каналы переписки.
Для тестирования всей статьи я собирался арендовать выделенный сервер на месяц и купить домен с ssl сертификатом.
Но жизненные обстоятельства сложились так этот вопрос затянулся на 2 месяца.
И вот когда появилось снова свободное время — решил публиковать статью как есть, а не рисковать тем, что публикация затянется ещё на год.
Если будет достаточно много вопросов типа «а вот тут не достаточно подробно описано» — тогда наверное найдутся силы таки взять выделенный сервер с новым доменом и новым SSL сертификатом и ещё подробнее описать и главное — выявить все упущенные важные детали.
Также хотелось бы получить отзывы на тему идеи про почтовые сертификаты. Если идея понравится — постараюсь найти силы написать черновик для rfc.
При копировании больших кусков статьи — указывать ссылку на эту статью.
При переводе на любой другой язык — указывать ссылку на эту статью.
На английский язык я сам постараюсь перевести и оставлю перекрёстные ссылки.
Postfix + Dovecot. SPF + DKIM + rDNS. С IPv6.
С шифрованием TLS. С поддержкой нескольких доменов — часть с настоящим 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.
При копировании больших кусков статьи — указывать ссылку на эту статью.
При переводе на любой другой язык — указывать ссылку на эту статью.
На английский язык я сам постараюсь перевести и оставлю перекрёстные ссылки.
