Как стать автором
Обновить

Как сделать VPN-туннель для недружественного почтового сервера

Время на прочтение18 мин
Количество просмотров19K

В предыдущей статье, где рассказывалось о признании моего почтового сервера «недружественным», я упоминал о том, что смог обойти введённые ограничения с помощью аренды дополнительного виртуального сервера на территории РФ и проброса до него VPN-туннеля от основного сервера. После публикации статьи мне пришло несколько личных сообщений с просьбой рассказать подробнее о процессе настройки такого туннеля. В этой статье я постараюсь как можно более подробно описать этот процесс.

Введение

В статье предполагается, что на всех серверах установлена ОС Ubuntu 20.04, а в качестве почтового сервера используется Postfix. В целом, описанные здесь методы, скорее всего, будут применимы к любому дистрибутиву Linux, а также и к другим UNIX-подобным ОС (например, FreeBSD), но вам нужно будет сделать поправки в части используемого менеджера пакетов, фаервола и, возможно, настроек сетевой маршрутизации.

Добавлю также, что VPN-туннель между серверами можно использовать не только для почты, но и для проксирования любого другого протокола, где важно знать оригинальный IP-адрес клиента и где невозможно получить его каким-то иным способом, кроме как средствами нижележащего протокола транспортного уровня (TCP или UDP). Например, для HTTP достаточно простого обратного прокси, так как в нём реальный IP можно передавать с помощью заголовков X-Forwarded-For. В случае с SMTP также возможна настройка промежуточного почтового сервера (релея), но по сравнению с поднятием VPN это гораздо более трудоёмкая задача, поэтому рассматривать такой вариант мы здесь не будем.

Сценарий использования

Наш сценарий использования можно описать следующим образом. У нас есть основной сервер (назовём его mail.mydomain.com), который принимает и отправляет электронную почту. При этом существует определённый почтовый домен (например, unfriendly.org), MX-сервера которого не принимают входящие соединения от mail.mydomain.com из-за его «недружественности», не позволяя ему слать почту на адреса @unfriendly.org. Приём почты от @unfriendly.org тоже не работает по аналогичным причинам.

Мы арендуем дополнительный сервер-шлюз (назовём его vpn.mail.mydomain.com), который для MX unfriendly.org «недружественным» не является, и хотим установить между нашим шлюзом и основным сервером VPN-туннель, чтобы иметь возможность обмениваться почтой между @unfriendly.org и mail.mydomain.com.

Отдельно отметим, что мы НЕ хотим, чтобы все исходящие соединения с mail.mydomain.com шли через VPN — через наш шлюз должны идти соединения только при отправке на @unfriendly.org (и другие проблемные домены, если они есть), а все остальные соединения должны идти напрямую. В итоге должна получиться следующая схема:

(создано на скорую руку в app.diagrams.net)
(создано на скорую руку в app.diagrams.net)

Для поднятия туннеля будем использовать OpenVPN. Я использовал информацию из вот этого руководства по его настройке в качестве стартовой точки. Сначала нам нужно установить пакет OpenVPN в систему. Рекомендую устанавливать его из официального репозитория, для этого выполняем следующие команды:

# wget -O - https://swupdate.openvpn.net/repos/repo-public.gpg | apt-key add -
# echo "deb http://build.openvpn.net/debian/openvpn/stable focal main" > /etc/apt/sources.list.d/openvpn-aptrepo.list
# apt update && apt install openvpn

Далее перемещаемся в папку /etc/openvpn/server и генерируем общий ключ для TLS-аутентификации (мы будем использовать его и на сервере, и на клиенте):

$ cd /etc/openvpn/server
$ sudo openvpn --genkey secret ta.key

Теперь создадим файл конфигурации сервера server.conf в той же папке. Ниже привожу содержимое этого файла с дополнительными комментариями:

# Понижаем привилегии процесса после запуска
# в целях безопасности (необязательно)
user nobody
group nogroup

# Используем виртуальный драйвер TUN (сетевой уровень)
dev tun

# Здесь я использовал нестандартный порт,
# но можно использовать любой другой.
# По умолчанию используется порт 1194
port 6876

# Рекомендуется использовать протокол UDP.
# Чтобы понять, почему - гуглим "TCP Meltdown"
proto udp

# Сертификаты и приватные ключи.
# Мы создадим их на следующих шагах данного руководства
ca ca.crt
cert vpn.mail.mydomain.com.crt
key vpn.mail.mydomain.com.key

# Настройки шифрования и аутентификации TLS.
# Мы будем использовать эллиптические кривые,
# поэтому алгоритм DHE можно отключить
dh none
tls-crypt ta.key
cipher AES-256-GCM
auth SHA512

# Сетевые настройки
topology subnet
server 172.16.0.0 255.255.255.0
keepalive 10 120

# В этой папке будем хранить настройку 
# статического IP-адреса для клиента
client-config-dir /etc/openvpn/server/ccd

# Необходимо при понижении привилегий
persist-key
persist-tun

# Отправляем уведомление клиенту при остановке/перезапуске сервера
explicit-exit-notify 1

# Уровень логирования
verb 3

Чтобы установить статический IP-адрес в VPN-сети для нашего почтового сервера, создадим папку /etc/openvpn/server/ccd и добавим туда файл mail.mydomain.com со следующей строчкой:

ifconfig-push 172.16.0.2 255.255.255.0

Локальный IP-адрес сервера (vpn.mail.mydomain.com) будет 172.16.0.1, клиента (mail.mydomain.com) — 172.16.0.2.

Дополнительно вы можете жёстко задать параметры шифрования для использования эллиптических кривых, особенно если у вас другая ОС с более старой версией OpenSSL, где могут поддерживаться не все современные наборы шифров. Как это сделать, см. здесь (спасибо за материал пользователю @Maxim_Q).

Настройка фаервола

В Ubuntu 20.04 в качестве фаервола используется ufw, который является фронтендом для популярного в Linux iptables. Помимо правил, добавляемых с помощью команд, в ufw есть конфигурационные файлы, позволяющие прописывать дополнительные правила в формате iptables. Откроем файл /etc/ufw/before.rules и добавим в начало следующие строчки:

*nat
-A PREROUTING -i eth0 -p tcp -m tcp --dport 25 -j DNAT --to-destination 172.16.0.2:25
-A POSTROUTING -s 172.16.0.0/24 -o eth0 -j MASQUERADE
COMMIT

Здесь добавляется два правила в таблицу NAT:

  1. -A PREROUTING ... перенаправляет входящие TCP-соединения на 25-й порт с интерфейса eth0 на адрес 172.16.0.2 (то есть на наш почтовый сервер mail.mydomain.com).

  2. -A POSTROUTING ... разрешает исходящие соединения из локальной VPN-сети на интерфейс eth0 (в интернет) путём настройки SNAT для исходящих пакетов.

NB: Проверьте (например, через ifconfig), что ваш основной сетевой интерфейс называется именно eth0. Если это не так, укажите в правилах корректное название.

Дополнительно нужно включить и разрешить форвардинг IPv4-пакетов. Для этого сначала отредактируем /etc/sysctl.conf и раскомментируем в нём строчку net.ipv4.ip_forward=1. Затем применим новые настройки с помощью команды:

# sysctl -p

Теперь в файле /etc/default/ufw находим строчку DEFAULT_FORWARD_POLICY="DROP" и меняем DROP на ACCEPT. Осталось только включить ufw и разрешить через него входящие SMTP- и OpenVPN-соединения. Не забываем также разрешить и SSH-соединения, чтобы не заблокировать себе доступ:

# ufw allow ssh
# ufw allow 25/tcp
# ufw allow 6876/udp
# ufw enable

NB: Не забудьте указать корректный номер порта OpenVPN вместо 6876, если вы используете другой порт (например, стандартый 1194).

Установка и настройка VPN-клиента

Сначала на машину mail.mydomain.com нужно установить пакет OpenVPN — делаем это таким же способом, как на VPN-сервере. Затем копируем сгенерированный на предыдущем этапе общий ключ ta.key с VPN-сервера в папку /etc/openvpn/client. Далее создаём конфигурационный файл /etc/openvpn/client/client.conf:

client

# Понижаем привилегии процесса после запуска
# в целях безопасности (необязательно)
user nobody
group nogroup

# Используем виртуальный драйвер TUN и протокол UDP
dev tun
proto udp

# Настройки подключения к удалённому серверу
remote vpn.mail.mydomain.com 6876
resolv-retry infinite
nobind

# Сертификаты и приватные ключи.
# Мы создадим их на следующих шагах данного руководства
ca ca.crt
cert mail.mydomain.com.crt
key mail.mydomain.com.key
remote-cert-tls server

# Настройки шифрования и аутентификации TLS
tls-crypt ta.key
cipher AES-256-GCM
auth SHA512
auth-nocache

# Скрипт для настройки маршрутизации после поднятия туннеля.
# Мы создадим его на предпоследнем этапе данного руководства
script-security 2
up openvpn_routes.sh

# Необходимо при понижении привилегий
persist-key
persist-tun

# Уровень логирования
verb 3

Замечание

Если вы понижаете привилегии процесса OpenVPN, то при остановке клиента или сервера через systemctl или при перезагрузке системы будете наблюдать в логе сообщения вида:

MMM dd HH:mm:ss mail.mydomain.com openvpn[123456]: RTNETLINK answers: Operation not permitted
MMM dd HH:mm:ss mail.mydomain.com openvpn[123456]: Linux ip addr del failed: external program exited with error status: 2

Объясню, в чём дело: OpenVPN хочет удалить маршрут к VPN-подсети из системы, но не может этого сделать из-за пониженных привилегий (с добавлением маршрута проблем нет, так как привилегии понижаются не сразу, а после завершения начальной настройки). Но это не является чем-то критичным, так как маршрут всё равно пропадает автоматически после удаления туннельного сетевого интерфейса, что как раз и происходит при остановке OpenVPN.

Если вы всё же хотите избавиться от этих сообщений, можно попробовать сделать это, например, вот так. Я этот метод не пробовал, поэтому не могу сказать, будут ли здесь какие-то ещё проблемы, но лично мне этот вариант кажется не очень безопасным, так как он, по сути, сводится к разрешению выполнения команды ip для непривилегированных пользователей.

Выпуск сертификатов для аутентификации

Альтернативный вариант

Если вам лень возиться с настройкой собственного центра сертификации, можно попробовать использовать другой способ аутентификации — статический ключ. Вот здесь описывается, как это сделать. Вам нужно будет подправить конфигурацию и удалить все упоминания о сертификатах, а также убратьclient-config-dir и прописать настройки IP-адресов вручную на стороне клиента и сервера. Убедитесь, что сервер запускается, путём выполнения команды:

# systemctl start openvpn-server@server

Если что-то пошло не так, проверьте лог:

# journalctl -u openvpn-server@server -e

Также стоит добавить сервер в автозагрузку с помощью следующей команды:

# systemctl enable openvpn-server@server

Если всё в порядке, то переходите к следующему разделу данного руководства «Настройка маршрутизации».

OpenVPN по умолчанию использует сертификатную аутентификацию. Это значит, что при подключении клиент и сервер должны обменяться X.509-сертификатами, подписанными доверенным удостоверяющим центром (УЦ). Такой центр мы создадим самостоятельно и сначала выпустим корневой сертификат, который установим и на сервер, и на клиент, а затем создадим и подпишем для них пользовательские сертификаты.

Почему мы не можем использовать для выпуска сертификата, например, сервис Let's Encrypt? Всё просто: если наш VPN-сервер будет доверять УЦ Let's Encrypt, то без дополнительных настроек кто угодно сможет запросить у них сертификат и использовать его для подключения к нашему VPN, чего мы, конечно же, не хотим. Поэтому мы развернём свой собственный центр сертификации, работу которого мы можем контролировать. В этом нам поможет пакет EasyRSA, представляющий собой обёртку над популярной утилитой OpenSSL.

Отметим также, что УЦ на базе EasyRSA необязательно устанавливать именно на VPN-сервер, более того, такой вариант менее безопасен, так как на сервере будет храниться приватный ключ вашего центра сертификации, которым можно подписывать пользовательские сертификаты и таким образом управлять доступом к вашему VPN-серверу. Оптимальный вариант — установить УЦ на ваш домашний компьютер, ноут и т.д. (если вы параноик, можете вообще отключить его от сети и передавать все данные на флешках).

Пакет EasyRSA используется не только для создания центра сертификации, но и для генерации приватных ключей и запросов на выпуск сертификатов. Работает это так:

  1. На сервере vpn.mail.mydomain.com генерируем пару «приватный ключ» — «запрос на сертификат».

  2. Отправляем файл запроса в УЦ.

  3. УЦ создаёт и подписывает сертификат и отправляет его обратно на сервер.

  4. Аналогичные операции проводим на машине mail.mydomain.com.

Обратите внимание, что ни один приватный ключ (в том числе ключ самого УЦ) не покидает машины, на которой он генерируется. Это одно из важных преимуществ использования криптосистемы с открытым ключом.

Установка центра сертификации

Как уже говорил ранее, этот шаг желательно выполнять на отдельной машине. Для упрощения будем полагать, что на ней также стоит Ubuntu 20.04.

Сначала установим пакет easy-rsa:

# apt update && apt install easy-rsa

Теперь создадим в домашнем каталоге папку easyrsa, в которой будем хранить настройки, сертификат и приватный ключ нашего УЦ:

$ cd ~
$ mkdir easyrsa
$ chmod 700 easyrsa

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

$ ln -s /usr/share/easy-rsa/* ~/easyrsa/

Начиная с версии 2.4, в OpenVPN возможно использование криптографии на эллиптических кривых, которая является более надёжной по сравнению с традиционным RSA. Чтобы использовать эллиптические кривые (в данном случае предлагается кривая secp521r1), создадим файл ~/easyrsa/vars со следующим содержимым:

set_var EASYRSA_ALGO "ec"
set_var EASYRSA_CURVE "secp521r1"
set_var EASYRSA_DIGEST "sha512"

Далее выполняем команды:

$ cd ~/easyrsa
$ ./easyrsa init-pki

На момент написания данного текста в версии OpenSSL в Ubuntu 20.04 есть небольшой баг, из-за которого при генерации ключей может появляться сообщение вида random number generator:RAND_load_file:Cannot open file (источник). Оно не свидетельствует о критической ошибке, но от него можно избавиться, создав указанный файл вручную:

$ dd if=/dev/urandom of=pki/.rnd bs=256 count=1

Осталось инициализировать сам УЦ:

$ ./easyrsa build-ca

В ходе создания УЦ вас попросят ввести парольную фразу — рекомендуется сделать её достаточно сложной, чтобы её нельзя было подобрать. Также нужно будет задать имя (Common Name) для вашего УЦ — можно использовать в качестве него, например, имя хоста вашей машины. Если вы пропустите этот шаг, по умолчанию будет использоваться имя «Easy-RSA CA», не являющееся особо примечательным, поэтому рекомендую вместо него задать своё имя — тогда при проверке выпущенных сертификатов будет сразу понятно, что они подписаны именно вашим УЦ.

Выпуск сертификата для VPN-сервера

Теперь займёмся выпуском сертификата для OpenVPN-сервера на машине vpn.mail.mydomain.com. Для этого на неё сначала необходимо установить пакет EasyRSA и выполнить все команды до init-pki включительно (см. выше). Далее нам необходимо сгенерировать приватный ключ и запрос на сертификат, который мы отправим в наш УЦ:

$ ./easyrsa gen-req vpn.mail.mydomain.com nopass

Обратите внимание на параметр nopass — если его не указать, то EasyRSA попросит вас задать парольную фразу для генерируемого ключа, и VPN-сервер каждый раз при запуске будет её запрашивать (по очевидным причинам нам такой вариант не подходит). Также в ходе выполнения команды gen-req у вас снова запросят имя (Common Name), на этот раз для будущего сертификата — тут можно просто нажать Enter, чтобы использовать имя, которое вы указали в параметрах команды. В итоге у вас должны сгенерироваться два файла:

~/easyrsa/pki/private/vpn.mail.mydomain.com.key — это приватный ключ. Его необходимо переместить в папку /etc/openvpn/server, предварительно сменив владельца на root:

$ cd ~/easyrsa/pki/private/
$ sudo -s
# chown root:root vpn.mail.mydomain.com.key
# mv vpn.mail.mydomain.com.key /etc/openvpn/server/

~/easyrsa/pki/reqs/vpn.mail.mydomain.com.req — это запрос на создание сертификата. Этот файл необходимо переместить на машину УЦ в любую папку (например, /tmp), затем на ней же выполнить команды:

$ cd ~/easyrsa
$ ./easyrsa import-req /tmp/vpn.mail.mydomain.com.req vpn.mail.mydomain.com
$ ./easyrsa sign-req server vpn.mail.mydomain.com

Убеждаемся, что Common Name в запросе на сертификат совпадает с именем хоста соответствующей машины (в данном случае vpn.mydomain.com) и, если всё верно, вводим «yes», после этого вас ещё попросят ввести парольную фразу, которую вы придумали ранее при создании УЦ. Сертификат будет сгенерирован по следующему пути: ~/easyrsa/pki/issued/vpn.mail.mydomain.com.crt. Берём его вместе с корневым сертификатом УЦ, который находится в ~/easyrsa/pki/ca.crt, и копируем на VPN-сервер в папку /etc/openvpn/server. Не забываем про настройки владельца и права доступа для этих файлов (chown root:root и chmod 600 соответственно).

На данном этапе настройка VPN-сервера завершена. Чтобы запустить сервер, выполняем на нём команду:

# systemctl start openvpn-server@server

Чтобы добавить сервер в автозагрузку, выполняем:

# systemctl enable openvpn-server@server

Если при запуске возникла ошибка, посмотреть лог можно с помощью следующей команды:

# journalctl -u openvpn-server@server -e

Наиболее частые ошибки возникают из-за проблем с конфигурацией (опечатки, пропуски) или отсутствия необходимых файлов (сертификат, приватный ключ и т.д.). Также, если вы неправильно настроили права доступа к файлам сертификатов и ключей, в логе появится предупреждение об этом.

Выпуск сертификата для VPN-клиента

Как и на VPN-сервере, на машине mail.mydomain.com тоже нужно установить пакет EasyRSA и выполнить init-pki. После этого создаём приватный ключ и запрос на сертификат:

$ ./easyrsa gen-req mail.mydomain.com nopass

Перемещаем сгенерированный приватный ключ из ~/easyrsa/pki/private/mail.mydomain.com.key в папку /etc/openvpn/client, не забывая про смену владельца:

$ cd ~/easyrsa/pki/private/
$ sudo -s
# chown root:root mail.mydomain.com.key
# mv mail.mydomain.com.key /etc/openvpn/client/

Затем отправляем файл запроса ~/easyrsa/pki/reqs/mail.mydomain.com.req на машину УЦ (например, в /tmp), импортируем, выпускаем сертификат:

$ cd ~/easyrsa
$ ./easyrsa import-req /tmp/mail.mydomain.com.req mail.mydomain.com
$ ./easyrsa sign-req client mail.mydomain.com

Ещё раз вводим «yes» (предварительно убедившись, что Common Name совпадает с mail.mydomain.com) и парольную фразу. Как и в предыдущий раз, сертификат создаётся по пути: ~/easyrsa/pki/issued/mail.mydomain.com.crt. Копируем его и корневой сертификат из ~/easyrsa/pki/ca.crt на машину mail.mydomain.com в папку /etc/openvpn/client, в очередной раз не забывая про настройку прав доступа.

На этом, казалось бы, всё, и VPN-клиент готов к работе, но остаётся ещё один момент — маршрутизация. Не забыли про строчку up openvpn_routes.sh в конфигурации клиента? Сейчас мы с этим разберёмся.

Настройка маршрутизации на VPN-клиенте

Напомню, что наш сценарий заключается в том, что направлять через VPN мы хотим не весь интернет-трафик, а только определённые соединения. Здесь есть небольшая проблема, так как обычно для всех исходящих интернет-соединений используется шлюз по умолчанию, ассоциированный с основным сетевым интерфейсом (например, eth0). Напомню, что на VPN-сервере мы специально не включали SNAT для входящих IP-пакетов, так как при подключении к почтовому серверу нам важно видеть реальный IP-адрес удалённой стороны вместо локального адреса VPN-сервера (172.16.0.1). Если ничего не настраивать, то при входящем соединении через VPN ответ этому IP-адресу, согласно правилам маршрутизации, почтовый сервер отправит через основной сетевой интерфейс. Очевидно, что ни к чему хорошему это привести не может.

Решением данной проблемы является создание альтернативной таблицы маршрутизации и задание правил для её использования. Источником для написания данного раздела послужила эта статья.

Сначала нам нужно указать системе, что мы создаём новую таблицу маршрутизации. В данном руководстве она будет называться openvpn. Создадим файл /etc/iproute2/rt_tables.d/openvpn.conf с единственной строчкой:

1   openvpn

Теперь создаём shell-скрипт /etc/openvpn/client/openvpn_routes.sh со следующим содержимым:

#!/bin/bash

ip route add default via 172.16.0.1 dev $dev table openvpn
ip rule add from 172.16.0.2 table openvpn
ip rule add to 172.16.0.2 table openvpn

Не забываем сделать его исполняемым:

# chmod +x /etc/openvpn/client/openvpn_routes.sh

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

  1. Команда ip route add default ... добавляет маршрут со шлюзом по умолчанию 172.16.0.1 (локальный IP нашего VPN-сервера) через интерфейс $dev в таблицу маршрутизации openvpn. Значение параметра $dev устанавливается самим клиентом OpenVPN перед запуском скрипта и содержит название туннельного интерфейса для VPN-соединения (обычно это tun0).

  2. Команда ip rule add from ... добавляет правило маршрутизации, предписывающее использовать таблицу openvpn для всех исходящих соединений с IP-адреса 172.16.0.2 (это локальный IP нашего VPN-клиента).

  3. Команда ip rule add to ... добавляет аналогичное правило маршрутизации, но оно относится уже к входящим соединениям.

Таким образом, после выполнения данного скрипта все соединения, где одной из конечных точек является локальный IP-адрес машины mail.mydomain.com в VPN-туннеле (т.е. 172.16.0.2), будут маршрутизироваться через этот туннель. Чтобы проверить это, сначала поднимаем клиент:

# systemctl start openvpn-client@client

Рекомендую также добавить его в автозапуск:

# systemctl enable openvpn-client@client

Если клиент стартовал без ошибок, для начала рекомендую попинговать 172.16.0.1, чтобы убедиться, что соединение с сервером действительно есть. Теперь попробуем соединиться через VPN с сервисом telnetmyip.com, чтобы убедиться, что маршрутизация работает корректно. С помощью telnet это можно сделать так:

$ telnet -b 172.16.0.2 telnetmyip.com

У меня этот сервис почему-то ничего не выводит, а просто закрывает соединение (возможно, из-за той же пресловутой «недружественности»). В этом случае можно попробовать ещё через curl:

$ curl --interface 172.16.0.2 telnetmyip.com

Вы получите ответ вида:

{


"comment": "##     Your IP Address is WW.XX.YY.ZZ (<номер_порта>)     ##",


"family": "ipv4",
"ip": "WW.XX.YY.ZZ",
...
}

Если WW.XX.YY.ZZ совпадает с внешним IP-адресом машины vpn.mail.mydomain.com, то поздравляю — всё настроено корректно! Замечу, что в случае некорректной настройки вам, скорее всего, вообще не удастся установить соединение.

Входящие соединения можно проверить, подключившись с помощью telnet к vpn.mail.mydomain.com на порт 25 с любой сторонней машины (например, с домашней — если, конечно, ваш интернет-провайдер не блокирует 25-й порт на выход). При этом в логах почтового сервера должен появиться ваш реальный внешний IP-адрес, а не локальный адрес VPN-сервера 172.16.0.1.

Настройка Postfix

Приём почты

Чтобы принимать в Postfix входящие соединения через VPN-туннель, вам не нужно менять никаких настроек — достаточно лишь добавить в DNS вторую MX-запись для вашего почтового домена, указав в ней имя хоста vpn.mail.mydomain.com. Рекомендуется поставить ей низкий приоритет (например, если для основной записи у вас приоритет 10, ставьте 100), чтобы соединения через VPN-шлюз шли только тогда, когда доступ к основному серверу заблокирован с удалённой стороны.

Единственное, что здесь стоит отметить — при соединении с vpn.mail.mydomain.com ваш сервер будет представляться как mail.mydomain.com. Это не должно повлиять на доставку почты, но если вы хотите сделать всё «по феншую», то нужно будет немного пошаманить с конфигурацией. Здесь есть два варианта, которые сводятся к тому, чтобы в /etc/postfix/master.cf создать дополнительный SMTP-сервер, который будет принимать соединения через VPN, и прописать ему нужное значение myhostname.

Первый вариант: привязать второй SMTP-сервер к IP-адресу в локальной VPN-сети, вот так это будет выглядеть в master.cf:

172.16.0.2:smtp      inet  n       -       y       -       -       smtpd
-o syslog_service_name=postfix/vpn
-o myhostname=vpn.mail.mydomain.com

Проблема здесь в том, что если VPN-туннель не поднят, при запуске Postfix выдаст фатальную ошибку: «fatal: bind 172.16.0.2 port 25: Cannot assign requested address». Поэтому я считаю такой способ не очень надёжным (например, если OpenVPN почему-то откажется запускаться, то Postfix тоже не сможет стартовать). Но можно поступить чуть хитрее, а именно — создать сервер, привязанный к другому порту. В примере ниже используется порт 4925 вместо стандартного 25:

4925      inet  n       -       y       -       -       smtpd
-o syslog_service_name=postfix/vpn
-o myhostname=vpn.mail.mydomain.com

Чтобы это заработало, на VPN-сервере нужно отредактировать файл /etc/ufw/before.rules: в правиле -A PREROUTING ..., которое мы создавали при настройке сервера, нужно поменять в конце порт 25 на 4925. Затем выполнить команду:

# service ufw restart

NB: Если после этого вы видите, что на стороне Postfix подключение идёт всё ещё на 25-й порт — попробуйте перезагрузить VPN-сервер.

Теперь при подключении к vpn.mail.mydomain.com сервер представится вам именно этим именем. Обратите внимание, что мы также указали для нового сервера syslog_service_name=postfix/vpn, чтобы в логе было видно, как маршрутизируется соединение:

MMM dd HH:mm:ss mail.mydomain.com postfix/vpn/smtpd[12345]: connect from unknown[WW.XX.YY.ZZ]

Если вы используете postscreen и хотите отвечать корректным именем при подключении через VPN, то помимо второго SMTP-сервера нужен будет второй postscreen с соответствующими настройками. Выглядеть это будет так:

4925      inet  n       -       y       -       1       postscreen
-o syslog_service_name=postfix/vpn
-o myhostname=vpn.mail.mydomain.com
-o smtpd_service_name=vpn-smtpd
-o postscreen_cache_cleanup_interval=0
vpn-smtpd  pass  -       -       y       -       -       smtpd
-o syslog_service_name=postfix/vpn
-o myhostname=vpn.mail.mydomain.com

Дополнительно вам нужно будет настроить postscreen так, чтобы он обращался к файлу кэша через proxymap, иначе получите фатальную ошибку из-за невозможности эксклюзивной блокировки этого файла. Добавляем в /etc/postfix/main.cf:

postscreen_cache_map = proxy:btree:/var/lib/postfix/postscreen_cache

и дело в шляпе.

Отправка почты

Для отправки почты через VPN-сервер в первую очередь, конечно, нужно убедиться, что разрешены исходящие соединения по 25-му порту и что у сервера всё в порядке с PTR, SPF и спам-листами. Если с этим проблем нет, то можно приступить к настройке.

Сначала создадим в /etc/postfix/master.cf отдельный SMTP-клиент и укажем ему значение smtp_bind_address, соответствующее локальному IP-адресу нашего почтовика в VPN-сети — в нашем случае это 172.16.0.2. По умолчанию, если Postfix не может выполнить биндинг на smtp_bind_address, он логирует предупреждение и повторяет попытку отправки уже с основного адреса. Postfix 3.7 содержит полезную настройку smtp_bind_address_enforce, которую мы можем включить для предотвращения подобного поведения: в этом случае, если VPN-туннель временно не работает, письмо просто вернётся в очередь для повторной отправки.

vpn-smtp   unix  -       -       y       -       -       smtp
-o syslog_service_name=postfix/vpn
-o myhostname=vpn.mail.mydomain.com
-o smtp_bind_address=172.16.0.2
# для Postfix 3.7 и выше
-o smtp_bind_address_enforce=yes 

Теперь настроим выборочную отправку писем на определённые домены через VPN. В нашем примере мы хотим отправлять через VPN письма на @unfriendly.org. Создаём файл /etc/postfix/vpn_transport со следующей строчкой:

unfriendly.org vpn-smtp

и добавляем в /etc/postfix/main.cf:

transport_maps = hash:/etc/postfix/vpn_transport

Далее выполняем команды:

# postmap /etc/postfix/vpn_transport
# systemctl reload postfix

и пробуем отправить письмо на любой адрес в домене unfriendly.org, затем убеждаемся, что при отправке этого письма вы видите в логе не postfix/smtp, а postfix/vpn/smtp. Аналогичным образом можно добавлять в /etc/postfix/vpn_transport другие почтовые домены, не забываем только каждый раз делать postmap и reload. Конечно, необязательно использовать именно формат hash для хранения транспортной таблицы — вы можете хранить эти данные в любом другом удобном вам формате (при условии, что он поддерживается в Postfix), в том числе в каком-нибудь MySQL/MariaDB.

Заключение

В данной статье мы рассмотрели простой способ создания альтернативного канала связи между почтовым сервером и внешним миром, который можно использовать, если по каким-то причинам (например, из-за «недружественности») связь с определёнными MX-хостами через основной канал не работает. Можно отметить следующие достоинства данного способа:

  • Простота настройки, особенно по сравнению с поднятием полноценного резервного почтового сервера (backup MX);

  • Возможность горизонтального масштабирования: при необходимости можно арендовать ещё один сервер и быстро поднять ещё один резервный канал — вследствие простоты настройки на это уйдёт совсем немного времени;

  • Никакие данные не хранятся на VPN-сервере, что особенно важно, если он расположен в РФ;

  • Для VPN-сервера можно выбирать самую дешёвую конфигурацию, так как OpenVPN не затрачивает большого количества ресурсов системы, если речь идёт об одном клиенте с небольшими объёмами трафика (что как раз применимо к нашему случаю).

Недостатком данного решения является привязка VPN-шлюза к конкретному почтовому серверу. Если ваша почтовая инфраструктура содержит несколько серверов, развёртывание туннеля может быть затруднено.

Ещё одной потенциальной проблемой, хоть и не связанной непосредственно с VPN, является ручная настройка списка «недружественных» почтовых доменов — при большом количестве пользователей на почтовом сервере это может быть просто неудобно. В теории можно попробовать автоматизировать этот процесс, написав свой собственный сервис, который будет пытаться устанавливать соединение с MX-серверами получателя с разных сетевых интерфейсов и, в зависимости от результатов, выбирать для передачи письма прямой SMTP-транспорт или VPN — но эта тема уже выходит за рамки данной статьи.

Теги:
Хабы:
Всего голосов 10: ↑10 и ↓0+10
Комментарии30

Публикации

Истории

Работа

Ближайшие события