Естественно в зависимости от того куда именно подключился в данный момент тот или другой клиент (отдающий свою сеть) должна нормально перестраиваться маршрутизация. То бишь быть динамической. К сожалению штатные механизмы 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

РЕШЕНИЕ:
Итого, собсно скрипт (быстро и на коленке).
Код: Выделить всё
#!/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
Код: Выделить всё
CL_ID=84
EXPORT_NET="10.0.84.0/24 10.0.89.0/24"
#DISABLED=YES
Сейчас нет защиты от дублирования, кроме ручного контроля уникальности и разделения диапазонов через 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
Подобный конфиг (с разными подсетями, типа там 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 *
* .... *
****************************
На закуску хочу оформить это дело таки в виде plugin-а, по мере преодоления граблей (см. ГРАБЛИ). Рассматривался вопрос реализации этого всего как управляющего демона через manadgement interface самого OpenVPN, там много плюсов, но централизация этого управления создает точку отказа, короче пока думаю
