И опять я пишу какое-то сомнительной нужности HOWTO вместо написания кода для зачёта по Win32API и MFC, граблеописание попросил сделать
u_magistr, пусть это будет на его совести.
Вкратце - будем добиваться устойчивости TCP-соединений к разрывам связи на последней миле и/или замены этой последней мили на лету. Опять таки у
омского вебстрима последнее время начались серьезные проблемы со связью и данная информация может оказаться весьма своевременной.
Всё нижеописанное применимо лишь в одном случае - у вас нестабильно работает интернет-провайдер, либо у вас динамический IP адрес и провайдер не предоставляет статического «белого» адреса за разумную цену. Также к недостаткам данного решения можно отнести дополнительную головную боль по поддержанию сервера в актуальном обновлённом состоянии и заметное увеличение сетевой задержки - в моём случае пинг до yandex.ru возрос со ста, до трёхсот миллисекунд, что обусловлено использованием находящегося в США ISP в качестве внешнего сервера. Дополнительные преимущества использования openvpn - шифрование трафика на последней миле и его сжатие. Краткий часовой эксперимент показал сжатие входящего трафика до 93% (сыграл роль просмотр пары роликов с YouTube) и исходящего до 68% (преимущественно текстового) от изначального объема при использовании алгоритма lzo.
Руководство по базовой настройке openvpn я рекомендую изучить на
сайте проекта openvpn, из особенностей я рекомендую настроить на клиенте использование сетевого интерфейса созданного на этапе загрузки, это сделает сетевую конфигурацию более статической и, соответственно, более простой. Я использую аутентификацию с помощью TLS-ключей, используя пакет easy-rsa, поставляемый вместе с openvpn - примеры настройки аутентификации можно найти на том же самом сайте.
На сервере я использую следующие довольно простые настройки.
gw:~# iptables -t nat -A POSTROUTING -s 172.27.0.176/255.255.255.240 -o ext_iface \
-j SNAT --to-source external.gw.example.org
gw:~# cat /etc/openvpn/server.conf
local external.gw.example.org
port 1194
proto udp
# Allow remote peer to change its IP address and/or port number
float
# На сервере я не использую фиксированный tun интерфейс - он создается динамически демоном openvpn
dev tun
# Как настоящий параноик я использую SSL.
ca ca.crt
cert gw.example.org.crt
key gw.example.org.key # на этот файл стоит поставить права 0600 или даже 0400
dh dh2048.pem
tls-auth tls-auth-20081020.key 0
server 172.27.0.176 255.255.255.240
# Тут настраивается резервирование статических IP адресов за клиентами для того,
# чтоб на стороне клиента можно было использовать статическую сетевую конфигурацию.
ifconfig-pool-persist ipp.txt 0
client-config-dir ccd
keepalive 30 90
# Та самая компрессия, о которой я говорил выше.
comp-lzo
max-clients 14
user nobody
group nogroup
persist-key
persist-tun
status openvpn-status.log
verb 4
За подробностями по плохо прокомментированным опциям отсылаю к оригинальному руководству или же жду ваших вопросов в комментариях.
Все самое интересное начинается на клиенте. Пускать весь трафик системы через openvpn-туннель - не самая лучшая идея, поэтому я решил настроить разделение трафика по UID-ам. Модуль для netfilter, согласно руководству, также умеет фильтровать трафик по имени процесса и другим интересным опциям, но все, кроме фильтрации по UID, поломано на SMP системах согласно тому же самому man iptables. Одной из подводных граблей при фильтрации по UID является то, что закрывающее соединение пакеты уже обрабатываются самой системой и, соответственно, не принадлежат определенному пользователю, следовательно, нужно использовать обработку пакетов на уровне соединений - нам для этого понадобится модуль netfilter под названием CONNMARK, а простого модуля MARK будет не достаточно.
Имя tun интерфейса положим tun_rw. Также не будем помечать трафик в «серые» сети - маршрутизировать их в интернет довольно неразумно.
iptables -t nat -A POSTROUTING -o tun_rw -j SNAT --to-source internal.host.example.org
iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark
iptables -t mangle -A OUTPUT -m mark --mark 0x1 -j RETURN
iptables -t mangle -A OUTPUT -m owner --uid-owner darkk -j TO_TUNNEL
iptables -t mangle -A TO_TUNNEL -d 10.0.0.0/8 -j RETURN
iptables -t mangle -A TO_TUNNEL -d 127.0.0.0/8 -j RETURN
iptables -t mangle -A TO_TUNNEL -d 169.254.0.0/16 -j RETURN
iptables -t mangle -A TO_TUNNEL -d 172.16.0.0/12 -j RETURN
iptables -t mangle -A TO_TUNNEL -d 192.168.0.0/16 -j RETURN
iptables -t mangle -A TO_TUNNEL -j MARK --or-mark 0x1
iptables -t mangle -A TO_TUNNEL -j CONNMARK --save-mark
Конфигурация openvpn:
tls-client
# имя того самого статического интерфейса, который создается при загрузке
dev tun_rw
proto udp
remote external.gw.example.org 1194
resolv-retry infinite
nobind
user openvpn
group openvpn
persist-key
persist-tun
persist-remote-ip
ca ca.crt
cert laptop.example.org.crt
key laptop.example.org.key
tls-auth tls-auth-20081020.key 1
ns-cert-type server
# Пинги посылаются каждые 30 секунд, отсутствие понгов в течение 90 секунд вызывает повторную инициализацию.
# Внимание! Понги не возвращаются - keepalive также должен быть указан на удаленной стороне
keepalive 30 90
comp-lzo
verb 3
И соответствующие конфигурации загрузки openvpn демона и iproute на примере init-скриптов gentoo:
======> /etc/conf.d/openvpn <======
# Переменная RC_NEED используется для того, чтоб не модифицировать сам init-скрипт.
RC_NEED="net.tun_rw"
======> /etc/conf.d/net <======
tuntap_tun_rw="tun"
tunctl_tun_rw="-u openvpn"
# Вот такая хитрая конфигурация для простой настройки openvpn...
# Сложность вызвана совместимостью с windows-клиентами, в FAQ openvpn указано, как избавится
# от этих костылей и упростить конфигурацию *nix-клиентов, если совместимость с windows не требуется
config_tun_rw=( "172.27.0.182 peer 172.27.0.181" )
routes_tun_rw=( "172.27.0.177 via 172.27.0.181" )
# Некоторая вспомагательная функция для более красивого вывода запускаемых команд.
run_commands() {
eindent
for x in "$@"; do
ebegin "${x}"
${x}
eend $?
done
eoutdent
}
postup() {
if [ "$IFACE" = "tun_rw" ]; then
einfo "Adding IP policy routing rules"
# rp_filter выключается, т.к. мы не добавляем default gateway на tun_rw,
# соответственно система не ожидает пакетов из интернета с этого интерфейса
# остальное - source-routing и маршрутизация по MARK-у conntrack-а
run_commands \
"ip route add table rw default dev tun_rw" \
"ip rule add fwmark 0x1 lookup rw" \
"ip rule add from 172.27.0.182 lookup rw" \
"sysctl -w net.ipv4.conf.tun_rw.rp_filter=0"
fi
}
postdown() {
if [ "$IFACE" = "tun_rw" ]; then
einfo "Removing IP policy routing rules"
run_commands \
"ip rule del from 172.27.0.182 lookup rw" \
"ip rule del fwmark 0x1 lookup rw"
fi
}
Из возможных граблей стоит отметить, что передача открытого соединения между процессами с разным UID-ом может поломаться при такой фильтрации. Те, кто про такую возможность вообще не слышали могут почитать man 7 unix на тему SCM_RIGHTS.
P.S. прошу прощения за сумбурность изложения - если есть вопросы,
задавайте их в комментариях, по мере возможности буду отвечать и править
HOWTO.