Отказоустойчивый OpenVPN и динамическая маршрутизация.

Настройка сетевых служб, маршрутизации, фаерволлов. Проблемы с сетевым оборудованием.
Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
slowstas
проходил мимо
Сообщения: 4
Зарегистрирован: 2014-03-23 17:24:19

Отказоустойчивый OpenVPN и динамическая маршрутизация.

Непрочитанное сообщение slowstas » 2014-03-23 20:58:42

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

Естественно в зависимости от того куда именно подключился в данный момент тот или другой клиент (отдающий свою сеть) должна нормально перестраиваться маршрутизация. То бишь быть динамической. К сожалению штатные механизмы OpenVPN (то бишь iroute в CCD) тут слабый помощник, ибо "нормальным" роутом оно никак не является, и никакими "внешними" тулзами типа quaggи не поддерживается. Это на случай tun интерфейсов, а с tap и того нету. Итого, нужно отслеживать состояние клиентских подключений средствами скриптов (или плагина) OpenVPN и менять маршрутизацию в самой OS, а потом все это разруливать кваггой. Я выбрал OSPF (ну вот просто так), ибо предыдущие танцы с RIP принесли излишний гемор.

ГРАБЛИ:
Как обычно началось все с граблей. У меня использовалась топология subnet и tun-ы, и оказалось что OpenVPN 2.2 и 2.3 заметно по разному с ними работает. Вплоть до того что на 2.2 tun выглядит как вroadcast интерфейс, а под 2.3 как P-t-P, и и это при коннекте к одному и тому-же серверу... Короче ospfd видели друг друга не всегда и сильно через раз, что меня утомило. И я волевым решением перешел на tap. Хотя в рамках одной версии возможно этих граблей и не будет. Вторые грабли - OpenVPN 2.3.2 из портов у меня упорно отказывается звать плагин на дисконнект клиента (хотя зовет при этом скрипт). Так что текущую реализацию пришлось делать на sh :) Кроме того при tap сам CCD стал по сути бесполезным, потому я решил его полностью заменить "своей" реализацией.

РЕШЕНИЕ:
Итого, собсно скрипт (быстро и на коленке).

Код: Выделить всё

#!/bin/sh
BASE_PATH=/usr/local/etc/openvpn
BASE_NET="10.0.102."
CCONFIG_DIR=/usr/local/etc/openvpn/ccdn
ROUTE_CMD=/sbin/route
CCDN_TEMP=/usr/local/etc/openvpn/ccdn.temp
LOG_FILE=/usr/local/etc/openvpn/cl.log


client_disconnect() {

if [ -f $CCDN_TEMP/$common_name ]; then
    echo client rtconf found $CCDN_TEMP/$common_name >> $LOG_FILE
    . $CCDN_TEMP/$common_name >> $LOG_FILE
    rm $CCDN_TEMP/$common_name
fi
}

client_connect() { 

if [ -f $CCDN_TEMP/$common_name ]; then
    rm $CCDN_TEMP/$common_name
fi

if [ -f $CCONFIG_DIR/$common_name ]; then
    . $CCONFIG_DIR/$common_name
    echo config found $CCONFIG_DIR/$common_name >> $LOG_FILE
    if [ $DISABLED ]; then 
	exit 1 
        echo disabled in config >> $LOG_FILE
    fi
    if [ $CL_ID ]; then
         CL_IP=$BASE_NET$CL_ID         
         echo ifconfig-push $CL_IP 255.255.255.0 >> $1
         echo force IP $CL_IP >> $LOG_FILE
    else
         CL_IP=$ifconfig_pool_remote_ip
    fi
    for i in $EXPORT_NET 
    do
	$ROUTE_CMD add -net $i $CL_IP -nostatic >> $LOG_FILE
        # для tun - тут нужно добавить и iroute , но мне лень возится с форматом маски... 
        #  echo iroute ...... >> $1
	echo "$ROUTE_CMD del -net $i $CL_IP -nostatic" >> $CCDN_TEMP/$common_name  
    done 
fi
}

echo `date` $script_type $common_name $trusted_ip $ifconfig_pool_remote_ip >> $LOG_FILE


case $script_type in
    "client-connect") client_connect $1
    ;;
    "client-disconnect") client_disconnect
    ;;
esac

exit 0
Где CCONFIG_DIR - "новая" версия CCD, с несколько иным форматом и функционалом.

Код: Выделить всё

CL_ID=84 
EXPORT_NET="10.0.84.0/24 10.0.89.0/24" 
#DISABLED=YES
CL_ID - позволяет в принудительно задавать клиентский IP (в рамках подсети сервера, см BASE_NET).
Сейчас нет защиты от дублирования, кроме ручного контроля уникальности и разделения диапазонов через ifconfig-pool, будем исправлять (TODO), пока к использованию не рекомендован.
EXPORT_NET - список экспортируемых сетей и именно в таком формате.
DISABLED (с любым непустым значением) - обламывает коннект клиента с auth-failed, служит для быстрого отключения сертификата.
Имя файла должно совпадать с common_name клиента, т.е. как и в "родном" CCD.

При установке маршрутов - команды на их убитие пишутся в специальный файлик (имя снова совпадает с common_name) в каталоге CCDN_TEMP, а при дисконнекте клиента - он просто выполняется. Я пока не нашел более разумного способа защититься от редактирования ccdn на лету....

Скрипт работает как client-connect и client-disconnect. Компатибильньный конфиг сервера:

Код: Выделить всё

port 1194
proto udp
dev tap2

server 10.0.102.0 255.255.255.0
#topology subnet
ifconfig-pool-persist /var/db/ipp.txt
status /var/db/openvpn-status.log
log-append /var/log/openvpn-server.log
cipher AES-256-CBC
client-to-client

# Все эти танцы для нормальной (и своевременной) отработки disconnect скрипта
ping 10
ping-exit 60
push "ping 10"
push "ping-restart 60"
push "explicit-exit-notify 3"
# вот и все танцы. 

dh dh2048.pem
ca ca.crt
cert xxxx.crt
key xxxx.key

#вся корпоративная сeть, c высокой метрикой, сильно больше чем виндовые дефолты.
push "route 10.0.0.0 255.255.0.0 10.0.102.1 500"  

# собсно скрипт.
script-security 2                                                    
client-connect /usr/local/etc/openvpn/clctl.sh       
client-disconnect /usr/local/etc/openvpn/clctl.sh   
Реализация весьма макетная ( и я буду ее допиливать) но это реально работает. С tap интерфейсом это будет работать, для tun - нужно добавить в скрипт установку iroute.
Подобный конфиг (с разными подсетями, типа там 10.0.101.0, 10.0.102.0 и 10.0.103.0) устанавливаются на собственно серверах, там-же должны жить ospfd. CCDN-ки должны быть ясно дело синхронизированны.Там-же поднимаются клиенты смотрящие на другие сервера. Для клиентов с работающим ospfd (включая и серверы) экспортировать сетки не нужно, квагга сама разберется. Где ospfd не стоит - для тех клиентов нужно прописать экспорт их сетей через ccdn. Можно с клиента поднимать линки на несколько серверов одновременно, но там желателен ospfd или хоть multipath routing (options RADIX_MPATH).

Код: Выделить всё

                                       Network 10.0.0.0/16

                                                    ***********************************
                                                    * vpn.corporate.net (R)           *
                                                    * single point over DNS(external) *
          **************************************    * or OpenVPN remote-random (X,Y,Z)*      *
          *  ospfd                             *    ***********************************
  +------>*  openvpn server 10.0.x.0 /24       *<XX  R
  |    +-<*  openvpn client  10.0.y.x          *  X  R<-----client without net   
  |    |  *  net 10.0.a.0/24                   *  X  R<-----client without net       
  |    |  **************************************  X  R<-----client net 10.0.d.0/24
  |    |                                          X  R   
  |    |                                          X  R   **************************** 
  |    |  **************************************  X  R   * client                   *
  |    |  *  ospfd                             *  X  RRR<* openvpn client (random)  *
  |    +->*  openvpn server 10.0.y.0/24        *<Y+YY+YY<* openvpn client (server y)*
  |    +-<*  openvpn client 10.0.z.y           *  X  R   * net 10.0.e.0/24          *
  |    |  *  net 10.0.b.0/24                   *  X  R   ****************************
  |    |  **************************************  X  R   
  |    |                                          X  R   ****************************
  |    |                                          X  R   * client                   *
  |    |  **************************************  XXX+XX<* openvpn client (server x)*
  |    |  *  ospfd                             *     R   * net 10.0.f.0/24          *
  |    +->*  openvpn server 10.0.z.0/24        *<ZZZZ+ZZ<* openvpn client (server z)*
  +------<*  openvpn client 10.0.x.z           *     R   ****************************
          *  10.0.c.0/24                       *     R   
          **************************************     R   ****************************
                                                     R   * client                   *
                                                     RRR<* openvpn client (random)  *
                                                         * net 10.0.g.0/24          *
                                                         * net 10.0.h.0/24          *
                                                         *  ....                    *
                                                         **************************** 


TODO: Причесывание скрипта и обработка ошибок (много причесывать), обработка iroute (для случая использования tun-ов), и упорядочивание работы с логами. в плане так-же хранение CCDN в LDAP (с их репликацией ессно). Возможно - установка роутов через zebrad, а не через /sbin/route но тут пока не уверен. И добавление работы с файерволом (через OpenVPN, есть там фичи про это).

На закуску хочу оформить это дело таки в виде plugin-а, по мере преодоления граблей (см. ГРАБЛИ). Рассматривался вопрос реализации этого всего как управляющего демона через manadgement interface самого OpenVPN, там много плюсов, но централизация этого управления создает точку отказа, короче пока думаю :)

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/