AdvancedVPN - OpenVPN с поддержкой авторизации и автоматическим переключением выходного шлюза

HELP-ME-24.COM (Freelance Team), Черноусов Антон
Как вы наверное знаете Госдума приняла закон о запрете сервисов по обходу блокировок сайтов. Не очень понятно как они теперь будут реализовывать то, что они сами придумали ведь создание собственного VPN-сервиса выйдет 15$ в год и это напомнило мне, что около года назад мне довелось поучаствовать в интересном проекте по построению довольно специфического VPN-сервиса для одной SEO-компании и я хотел бы поделиться общим описанием проекта.

Общая схема проекта.

Система предназначена для автоматического переключения маршрута исходящего трафика через внешний IP-адрес.

Схема AdvancedVPN

Выбор маршрута осуществляется путем выбора случайного сервера маршрутизации и дополнительного адреса назначенного серверу. В качестве выходных точек трафика могут выступать как отдельные VPS-серверы с поддержкой создания дополнительных TAP/TUN-интерфейсов, так и специализированные OpenVPN-сервера выполняющие аналогичный функционал.

Принцип работы.

Клиент подключается с использованием программного обеспечения OpenVPN-клиент. OpenVPN-клиент настроен на работу с использованием авторизации пользователя парой логин/пароль.

В момент подключения к серверу, производится сверка пары логин/пароль предоставленной клиентом и данными хранящимися в базе данных. В случае удачной авторизации пользователю изменяется шлюз по умолчанию для перенаправления всего исходящего трафика на центральный сервер маршрутизатор.

На сервере трафик клиента еще раз маршрутизируется на один из LXC-контейнеров (случайным образом), которой в свою очередь подключен к внешнему роутеру (VPS-сервер с дополнительными IP-адресами) для последующего перенаправления трафика.

В результате получается, что у клиента при каждом новом подключении будет случайный IP-адрес из пула адресов настроенных на маршрутизаторе.

Инфраструктура проекта

Центральный сервер:

IP-adress, allocated to your Virtual Server: 185.39.5.9

Your VDS adress: snuff.example.com

Маршрутизаторы с назначенным пулом IP-адресов:

MSK-RU-OVZ-1024 (1024МБ, 1 ядро, 30ГБ SAS+SSD, 100Мбит/с)

MSK-RU-KVM-1024 (1024МБ, 1 ядро, 30ГБ SAS+SSD, 100Мбит/с)

MSK-RU-KVM-1024 (1024МБ, 1 ядро, 30ГБ SAS+SSD, 100Мбит/с)

Техническое описание проекта

Структура базы данных:

Для хранения сведений о доступных маршрутах и информации о учетных данных пользователей используется база данных Postgresql содержащая две таблицы.

Первая таблица, users содержит сведения о учетных данных пользователей:

CREATE TABLE users( login character varying(40) NOT NULL, password character varying(80), int_ip character varying(80), CONSTRAINT users_pkey PRIMARY KEY (login))WITH ( OIDS=FALSE);ALTER TABLE users OWNER TO vpn_manager;

Поля login и password соответственно хранят данные о учетной записи пользователя используемые при авторизации, поле int_ip содержит значение внутреннего ip-адреса назначаемого клиенту.

Вторая таблица, routers содержит сведения о допустимых маршрутах и активных ротурах (VPS-серверах и дополнительных IP-адресах):

CREATE TABLE routers( lxc_name character varying(240) NOT NULL, enabled boolean DEFAULT false, active boolean DEFAULT true, route_table integer, gate_ip character varying(80), CONSTRAINT routers_pkey PRIMARY KEY (lxc_name))WITH ( OIDS=FALSE);

Используются поля:

  • lxc_name — имя контейнера маршрутизатора
  • enabled — принудительное включение или отключение маршрута
  • active — состояние маршрута (устанавливается автоматически путем периодической проверки состояния туннелей до VPS)
  • route_table — номер таблицы маршрутов (целое число)
  • gate_ip — ip-адрес lxc-контейнера

Конфигурация серверной части OpenVPN (блок подключения клиентов):

port 1443proto tcpdev tap-unlockserver 172.16.0.0 255.255.0.0ifconfig-pool-persist ipp.txtduplicate-cnmanagement localhost 8329client-config-dir /opt/manager/ccd/keepalive 10 60script-security 3auth-user-pass-verify /opt/manager/user-auth.py via-env
up /opt/manager/bridge_up.sh
username-as-common-name
comp-lzopersist-keypersist-tunverb 3
dh dh2048.pem
ca ca.crtcert vcenter-server.crt
key vcenter-server.key

Конфигурация блока OpnVPN достаточно типовая за исключением системы авторизации клиентов. За авторизацию клиентов с автоматической переконфигурацией отвечает скрипт /opt/manager/user-auth.py

#!/usr/bin/python3 import settingsimport psycopg2import ccdimport sysimport osresult=False try: cn = psycopg2.connect("dbname='"+settings.DATABASES['vpn_manager']['NAME']\
+"' user='"+settings.DATABASES['vpn_manager']['USER']\
+"' password='"+settings.DATABASES['vpn_manager']['PASSWORD']\
+"' host='"+settings.DATABASES['vpn_manager']['HOST']+"'")
cr=cn.cursor() user_name=os.environ['username'] cr.execute('SELECT password FROM users WHERE login=%s;',(user_name,)) passwd=cr.fetchone()[0] if passwd==os.environ['password']: ccd.rebuild_ccd(cr,user_name) result=True cr.close() cn.close()except: result=False if result: sys.exit(0)else: sys.exit(1)

Скрипт производит сверку имени пользователя и пароля с данными хранящимися в базе данных и в случае совпадения запускает модуль настройки клиента.

Конфигурация клиента хранится в каталоге /opt/manager/ccd/ и перестраивается при удачной авторизации на основании данных хранящихся в базе данных. За генерацию конфигурационных файлов клиента отвечает скрипт /opt/manager/ccd.py.

#!/usr/bin/python3import psycopg2import settingsfrom subprocess import call def rebuild_ccd (cr,user): cr.execute('SELECT int_ip FROM users WHERE login=%s;',(user,)) cn_name='/opt/manager/ccd/'+user ccd_file=open(cn_name,'w') ip=cr.fetchone()[0] ccd_file.write('ifconfig-push '+ip+' 255.255.0.0 \n') ccd_file.write('push "redirect-gateway def1" \n') ccd_file.close() call(["/opt/router/set_random_route.sh", ip]) return True

В результате работы скрипта клиенту назначается статический ip-адрес из внутренней подсети заданный в базе данных авторизации и настраивается перенаправление всего трафика на шлюз.

Так же данный скрипт вызывает подсистему настройки маршрута, которая собственно и задает LXC-контейнер который будет использоваться для маршрутизации.

Скрипт находится в файле /opt/router/set_random_route.sh

#!/bin/sh ip=$1 old_table=`ip rule list | grep "$ip" | awk '{ print $ 5 }' | tr -d " " `#echo "OLD: ""$old_table" randome_table=`su postgres -c "echo \"SELECT route_table FROM routers WHERE \
enabled=True AND active=True ORDER BY RANDOM() LIMIT 1;\" | psql -U postgres -d vpn_manager"\
| sed '1,2d' | head -n -2 | tr -d " "
while true; do if [ "$randome_table" = "$old_table" ]; then randome_table=`su postgres -c "echo \"SELECT route_table \
FROM routers WHERE enabled=True AND active=True ORDER BY RANDOM() LIMIT 1;\"\
| psql -U postgres -d vpn_manager" | sed '1,2d' | head -n -2 | tr -d " "`
else #echo "SET TABLE" break fi done;
ip rule del from $ip/32ip rule add from $ip/32 table $randome_tableexit 0

Одной из особенностей скрипта является, то что маршрут клиента можно изменить без переподключения, а просто запустив скрипт указав в качестве параметра внутренний ip-адрес подключенного клиента. Так же скрипт старается исключить повторную выдачу ip-адресов.

Скрипты сопровождения:

Помимо основных скриптов отвечающих за авторизацию клиента и модификации его маршрутов имеется ряд дополнительных скриптов отвечающих за функционирование всей инфраструктуры.

Во первых скрипт /etc/firewall.sh отвечающий за настройку параметров ip-tables. Обратите внимание, что forward-трафика должен быть обязательно активирован:

#!/bin/sh########################## Настройки интерфейсов #########################IF_EXT="eth0"IPT="/sbin/iptables" ##################### Правила iptables ##################### # Удаляем все старые правила$IPT -F$IPT -X$IPT -t nat -F$IPT -t nat -X$IPT -t mangle -F$IPT -t mangle -X # Настраиваем политики по умолчанию$IPT -P OUTPUT ACCEPT$IPT -P INPUT DROP$IPT -P FORWARD ACCEPT # Разрешаем все операции с loopback интерфейса$IPT -A INPUT -i lo -j ACCEPT$IPT -A FORWARD -o lo -j ACCEPT # Поддерживаем уже установленные соединения$IPT -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT$IPT -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT # Разрешаем ICMP-пинг (входящий)#$IPT -A INPUT -p icmp -j ACCEPT # Разрешаем доступ по SSH (все интерфейсы)$IPT -A INPUT -p tcp --dport 22 -j ACCEPT # Admin interface$IPT -A INPUT -p tcp --dport 8000 -j ACCEPT # Маскардинг трафика lxc-контейнеров$IPT -t nat -A POSTROUTING -o $IF_EXT -j MASQUERADE # VPN-Server$IPT -A INPUT -p tcp --dport 1443 -j ACCEPT exit 0

Для реализации взаимодействия между двумя интерфейсами используется proxy_arp и предварительное создание набора дополнительных таблиц маршрутизации для реализации модели роутинг на базе исходного адреса. Все эти операции выполняются при старте сервера и прописаны в файле /etc/rc.local:

#!/bin/sh echo 1 > /proc/sys/net/ipv4/conf/lxc-bridge/proxy_arp > /dev/null 2>&1echo 1 > /proc/sys/net/ipv4/conf/tap-unlock/proxy_arp > /dev/null 2>&1 /opt/router/start_routers.sh exit 0

Скрипт предварительной инициализации маршрутов /opt/router/start_routers.sh должен запускаться при добавлении новых LXC-контейнеров и систем спутников через которые осуществляется маршрутизация трафика.

#!/bin/shsu postgres -c "echo \"SELECT lxc_name, route_table, gate_ip FROM routers \
WHERE enabled=True;\" | psql -U postgres -d vpn_manager" | sed '1,2d' | \
head -n -2 | while read line;
do router=`echo $line | awk -F "|" '{ print $1 }'` table=`echo $line | awk -F "|" '{ print $2 }'` gate_ip=`echo $line | awk -F "|" '{ print $3 }'` if [ -n "$gate_ip" ]; then lxc-start --name $router ip route add default via $gate_ip table $table fi done

В каталоге /opt/routers находятся дополнительные скрипты предназначенные для упрощения создания LXC-контейнеров и подготовки маршрутизаторов трафика (VPS-серверов с дополнительными IP-адресами).

Скрипт /opt/router/start_routers.sh отвечает за запуск LXC-контейнеров маршрутизаторов при старте сервера. Запускаются только контейнеры отмеченные как enabled:

#!/bin/shsu postgres -c "echo \"SELECT lxc_name, route_table, gate_ip FROM routers \
WHERE enabled=True;\" | psql -U postgres -d vpn_manager" | sed '1,2d' \
| head -n -2 | while read line;
do router=`echo $line | awk -F "|" '{ print $1 }'` table=`echo $line | awk -F "|" '{ print $2 }'` gate_ip=`echo $line | awk -F "|" '{ print $3 }'` if [ -n "$gate_ip" ]; then lxc-start --name $router ip route add default via $gate_ip table $table fi done

Так же в каталоге имеется несколько скриптов авто создания контейнеров маршрутизаторов. Например /opt/router/clone_router.sh клонирует исходный контейнер и перенастраивает его в соответствии с передаваемыми параметрами:

#!/bin/shecho "Clone Router"router_name=$1router_ip=$2connect_to=$3 if [ -z "$connect_to" ]; then echo "Для клонирования роутера из шаблона используется следующий синтаксис:" echo " clone_router <routen-name> <router-ip> <connect-to>" echo " например: clone_router.sh route-21 10.200.0.21 37.46.128.190:444 " echo "=====================================================================" echo "Некоррекный синтаксис комманды!" exit fi lxc-copy --name route-template -N $router_namesed -i -r "s/\[IP-ADDRESS\]/$router_ip/" /var/lib/lxc/$router_name/rootfs/etc/network/interfacesip=`echo $connect_to | awk -F":" '{ print $1 }'`port=`echo $connect_to | awk -F":" '{ print $2 }'`ip_port=`echo $ip $port`sed -i -r "s/\[UPLINK\]/$ip_port/" /var/lib/lxc/

Конфигурация роутеров (VPS-сервера с дополнительными IP-адресами):

Все роутеры трафика работают по одному и тому же принципу и используют штатный механизм с перенаправлением через себя всего трафика подключенного клиента. Данную схему мы реализовывали для реализации механизма совместимости с с другими поставщиками VPN-решений.

Типовая схема реализации маршрутизации трафика через OpenVPN сервер использует механизм NAT и собственно сам OpenVPN в режиме изменения основного шлюза. В этом случае весь трафик клиента перенаправляется на сервер OpenVPN, где уже направляется далее в сеть Internet с подменой адреса источника.

Простая схема с перенаправлением всего трафика клиента выглядит следующим образом:

Схема простого VPN-сервера

В простейшем виде конфигурация OpenVPN для такой схемы будет следующая:

port 1194 proto tcp dev tun ca /etc/openvpn/easy-rsa/keys/ca.crt cert /etc/openvpn/easy-rsa/keys/server.crt key /etc/openvpn/easy-rsa/keys/server.key # This file should be kept secret dh /etc/openvpn/easy-rsa/keys/dh2048.pem server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt duplicate-cn keepalive 10 120 comp-lzo persist-key persist-tun verb 3 push "redirect-gateway def1" push "dhcp-option DNS 8.8.8.8"

Переопределение основного шлюза определяется параметрами:

push "redirect-gateway def1" push "dhcp-option DNS 8.8.8.8"

А маскардинг (NAT-трафика) для последующей отправки в интернет с подменой внутреннего адреса на внешний адрес Internet-сервера реализуется командой IP-tables:

iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE

В нашем случае можно использовать эту схему, но она не подразумевает использования дополнительных IP-адресов с маршрутизацией трафика через них.

В нашем случае сателлиты конфигурируются на этапе загрузки с назначением адресов алиасов и маршрутизацией NAT-трафика по несколько усложненной схеме с маршрутизацией по подсети источника. При старте сервера инициализируются настройки Firewall при помощи скрипта /etc/firewall.sh:

#!/bin/sh iptables -t nat -F iptables -t nat -X iptables -t mangle -F iptables -t mangle -X /sbin/ifconfig ens3:1 185.43.7.126 netmask 255.255.254.0 /sbin/ifconfig ens3:2 185.43.5.29 netmask 255.255.254.0 /sbin/ifconfig ens3:3 212.109.194.231 netmask 255.255.252.0 /sbin/ifconfig ens3:4 212.109.194.38 netmask 255.255.252.0 /sbin/ifconfig ens3:5 212.109.192.228 netmask 255.255.252.0/sbin/ifconfig ens3:6 83.220.172.222 netmask 255.255.254.0/sbin/ifconfig ens3:7 37.46.132.140 netmask 255.255.254.0/sbin/ifconfig ens3:8 37.46.130.7 netmask 255.255.254.0/sbin/ifconfig ens3:9 185.60.134.207 netmask 255.255.255.128/sbin/ifconfig ens3:10 185.43.4.32 netmask 255.255.254.0/sbin/ifconfig ens3:11 185.43.7.141 netmask 255.255.254.0/sbin/ifconfig ens3:12 185.43.7.136 netmask 255.255.254.0/sbin/ifconfig ens3:13 185.43.6.198 netmask 255.255.254.0/sbin/ifconfig ens3:14 185.43.5.213 netmask 255.255.254.0/sbin/ifconfig ens3:15 212.109.192.37 netmask 255.255.252.0/sbin/ifconfig ens3:16 37.46.131.172 netmask 255.255.254.0 /sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.169.0.1/24 -j SNAT --to 185.60.134.207/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.0.1/24 -j SNAT --to 185.43.7.126/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.1.1/24 -j SNAT --to 185.43.5.29/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.2.1/24 -j SNAT --to 212.109.194.231/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.3.1/24 -j SNAT --to 212.109.194.38/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.4.1/24 -j SNAT --to 212.109.192.228/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.5.1/24 -j SNAT --to 83.220.172.222/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.6.1/24 -j SNAT --to 37.46.132.140/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.7.1/24 -j SNAT --to 37.46.130.7/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.8.1/24 -j SNAT --to 185.60.134.207/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.9.1/24 -j SNAT --to 185.43.4.32/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.10.1/24 -j SNAT --to 185.43.7.141/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.11.1/24 -j SNAT --to 185.43.7.136/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.12.1/24 -j SNAT --to 185.43.6.198/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.13.1/24 -j SNAT --to 185.43.5.213/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.14.1/24 -j SNAT --to 212.109.192.37/sbin/iptables -t nat -A POSTROUTING -o ens3 -s 192.168.15.1/24 -j SNAT --to 37.46.131.172exit 0

Данным скриптом инициализируются алиасы интерфейса в соответствии с данными настройки выданными провайдером и настраивается схема маршрутизации по подсети входящего трафика.

В файле /etc/openvpn/ находятся конфигурационные файлы обеспечивающие подключение контейнеров LXC-с центрального сервера к роутеру. Эти конфигурационные файлы полностью идентичны и отличаются только подсетью подключения и связкой этой подсети с внешним адресом который будет использоваться как исходящий при маршрутизации трафика клиента.

Все файлы имею унифицированное название согласно подсети которую они обслуживают, например /etc/openvpn/vpn-192-168-11.conf:

port 455proto tcpdev tap-unlock-11server 192.168.11.0 255.255.255.248keepalive 10 60script-security 2up /etc/firewall.shcomp-lzopersist-keypersist-tunverb 3push "redirect-gateway def1" <dh>-----BEGIN DH PARAMETERS-----MIIBCAKCAQEA/6QAOYAHbKxpSu+gzY0tSfs/Xcbs7agqZgn4OX/uCw2XHM0N -----END DH PARAMETERS----- </dh> <ca>-----BEGIN CERTIFICATE-----MIIEwTCCA6mgAwIBAgIJAMxn4M3vmH1aMA0GCSqGSIb3DQEBCwUAMIGbMQswCQYdEp4CP7sBlsAS+skwQZrY39H5t3u-----END CERTIFICATE-----</ca> <cert>-----BEGIN CERTIFICATE-----MIIFPTCCBCWgAwIBAgIBAjANBgkqhkiG9w0BAQsFADCBmzELMAkGA1UEgfOslGYb3bcdstpJwj2ii9dlxAiBDiqErAFTMrK24IEs4qEDNXDLpqmKPUsqzVLTwLWbXY6itSLPx4ypSqwvy2Vn9KdGUHdFaIN96x1ykt2iQGoIOpmjmF7dFT4v8xIfw==-----END CERTIFICATE-----</cert> <key>-----BEGIN PRIVATE KEY-----S95cdQfWm1ONBDeOZn1W91n2hkTkUhV3wOg7yfO17vo4mw/DOfgemkZ+imhUIdnOzZqgcnFAIrNRsCzUmfQ7rH0=-----END PRIVATE KEY-----</key>

Конфигурационные файлы генерируются или в ручную или при помощи скрипта на центральном сервере.

Примечание.

При большом количестве LXC-контейнеров рекомендую для хранения файлов данных контейнеров использовать файловую систему с поддержкой дедупликации файлов. Так как все контейнеры фактически клонированны 99% процентов файлов там идентичны и можно значительно уменьшить объем занимаемого дискового пространства.

 

Оставьте комментарий

Вы должны быть вошедший в чтобы отправить комментарий