«Не единого разрыва» HOWTO

Dec 01, 2008 01:48


И опять я пишу какое-то сомнительной нужности 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.

linux, iproute, howto, iptables, openvpn, webstream

Previous post Next post
Up