Radmin за NAT (Менеджер переадрисации портов)

Добрый день!Еесть у меня один клиент (мой знакомый), который оказывает удалённую техподдержку по своей программе. Долгое время он пользовался "teamviewer" и "ammyy admin", его всё устраивало, но с каждым разом все больше зажимали бесплатный режим, а всякие шаманские методы конечно можно применить, только это временно. Ещё была часть клиентов на radmin, но для radmin нужен белый ip адрес, что порой бывает не у всех; а у кого белый ip - не всегда есть возможность настроить переадресацию. В итоге из 150 клиентов остаются только 50, к которым можно спокойно подключаться по radmin, а для остальных 100 - шаманские методы. Конечно, можно купить teamviewer или ammyy admin, но цена у них слишком высока, и соответственно нужно будет поднимать и цену обслуживания. В результате я ему предложил использовать VPN, ведь по vpn можно обходить nat, не требуется переадресации, не нужен белый ip клиента. Есть куча всяких vpn, например "Hamachi", но опять же свои заморочки.

Клиент попросил, чтоб это было максимально прозрачно для него, чтобы он мог подключиться быстро, с любого компа. С vpn я работал не раз и делал на них сеть (в частности OpenVPN) - вещь надёжная, куча настроек, может работать через прокси, бесплатный, вообще просто находка. Сервер я поднял без проблем не в первый раз, чтоб упростить установку и настройку клиентов, я сделал авторизацию по паролю (по сертификату есть не удобства, таскай сертификаты, генерировать новые сертификаты для новых клиентов, а с паролями всё просто). Дальше сделал просто - создал базу на MySQL, написал маленькую оснастку на Web для добавления и удаления пользователей, в общем для управления. Дальше оставалось заставить OpenVPN брать пароли с базы. В интернете я нашёл готовый скрипт, изучив его - написал свой скрипт, всё подключилось, зашуршало. Теперь можно подключать клиентов. Но если заказчику нужно подключиться к любому компу, то ему нужно тоже установить своё подключения, в результате таскать с собой openvpn (везде его ставить как бы не сильно проблематично, но хочется, чтоб было всё прозрачно). Ведь чтоб заказчику корректно подключаться к каждому его клиенту, нужно быть в сети vpn, что подразумевает админское подключение к OpenVPN. Для заказчика это лишние хлопоты, желательно, чтобы заказчик мог подключиться с любого места, а не ставить кучу софтов, сертификатов, в общем, чтоб было прозрачно, Тут мне пришла такая идея - сделать банальную переадресацию из внешнего интерфейса в интерфейс vpn. Ведь белый ip у нас есть у сервера, а значит можно сделать переадресацию, в которой мы укажем необходимость переадресации пакета с внешнего ip в vpn на клиента. Ведь мы можем сколько угодно переадрисовывать порты. Например, делаем вот такое правило:
(Клиент A)
iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 42000 -j DNAT --to-destination 192.168.22.10:4899 

Где мы говорим, что если пришёл пакет на внешний интерфейс eth0 на порт 42000, то отправляй его на Ip vpn 192.168.22.10 на порт 4899 клиента.Если нужен ещё клиент, делаем то же самое, но меняем порт.
(Клиент B)
iptables -t nat  -A PREROUTING -i eth0 -p tcp -m tcp --dport 42001 -j DNAT --to-destination 192.168.22.6:4899 

Теперь, чтобы подключиться к клиенту A, заходим в radmin, вписываем ip внешний, который на интерфейсе eth0, порт 42000. И мы должны попасть к клиенту A. Прописав всё это пробуем подключиться к клиенту, но подключения не происходит, а почему? Всё дело в том, что пакет, который переадресовывается клиенту, доходит до него, но обратно уходит со шлюза по умолчанию, поскольку нет такой сети, ведь мы подключаем с внешнего ip. Нужно сделать так, чтобы пакет, который пришёл с vpn, туда же и ушёл. Есть простой вариант - изменять все пакеты, а именно ip источника, мы берём и меняем с ip внешнего на ip внутреннего vpn.
Делается это простым правилом:
iptables -t nat -A POSTROUTING -d 192.168.22.0/23 -o tun0 -j SNAT --to-source 192.168.22.1

Теперь все пакеты, которые будут уходить с сервера в туннель, всегда будут иметь ip 192.168.22.1. Поскольку ip мы поменяли на внутренний, то пакет от клиента вернётся назад, опять произойдёт подмена из ip внутреннего на ip внешний. Это называется двойной NAT. Теперь мы спокойно подключимся. Но осталась ещё одна проблема: чтобы добавить правила для переадресации, нужно знать ip, куда переадресовывать. Тут и возникает вопрос: как узнать ip клиента. Можно его закрепить за клиентом, но тогда опять придётся добавлять для каждого пользователя новый файл конфигурации; после добавления файла конфигурации клиента нужно перезапустить сервер openvpn, а это не есть хорошо. Я пошёл по другому пути - написал некую систему, которая сама отслеживает клиента и добавляет ему правила.
Итак, приступим к практике.

Первое, что нужно сделать - настроить OpenVPN. Я не буду рассказывать, как сгенерировать серверные сертификаты и так далее, вот мой файл конфигурации:
# vi /etc/openvpn/server.conf
; Работаем в режиме демона
daemon openvpn_server
; Указываем местоположение файла с уникальным идентификатором процесса сервера OpenVPN
#writepid /var/openvpn/pid
; Определяем файл, содержащий список текущих клиентских соединений
#status /var/openvpn/status 10
; Внешний IP-адрес нашего сервера
#local 213.167.XX.YY
; Используемый порт
port 1194; Для транспорта по умолчанию применяется протокол UDP. Это
;вполне резонный подход. Во-первых, благодаря меньшему размеру заголовков и
;отсутствию встроенной функции подтверждения доставки пакетов, производительность
;UDP значительно выше. А во-вторых, при использовании UDP общая надежность не
;снижается, так как OpenVPN шифрует исходные пакеты, обеспечивая проверку ошибок
;и повторную передачу. В связи с этим TCP рекомендуется применять только в тех
;случаях, когда UDP не работает (например, если брандмауэр блокирует весь
;UDP-трафик).
proto tcp
; Выбираем тип виртуального устройства туннеля (tun, tap или null)
dev tun0
; Включаем компрессию передаваемых данных
;comp-lzo
; Указываем абсолютные пути к созданным сертификатам и ключам
ca /opt/vpn/openvpn/keys/ca.crt
cert /opt/vpn/openvpn/keys/server.crt
key /opt/vpn/openvpn/keys/server.key
dh /opt/vpn/openvpn/keys/dh1024.pem
; Значение 0 следует использовать на сервере, 1 - на клиенте
;tls-auth /usr/share/doc/openvpn/examples/easy-rsa/2.0/keys/ta.key 0
; Для большинства сетей подойдет режим маршрутизатора
;(routed). Да, в этом случае широковещательный трафик отсутствует,
;соответственно, не будут работать некоторые протоколы, которые его используют,
;например NetBIOS поверх TCP. Но последнее нивелируется развертыванием
;WINS-сервера, либо подключением сетевых дисков/созданием ярлыков на расшаренные
;ресурсы
server 192.168.22.0 255.255.254.0
; «Проталкиваем» подключенным клиентам статические маршруты,
;чтобы они могли получить доступ к ресурсам проводной и беспроводной сети
push "route 192.168.22.0 255.255.254.0"
#push "route 192.168.200.0 255.255.255.0"
; Не позволяем соединению разорваться при простое
keepalive 10 120
; Алгоритмы, используемые для аутентификации и шифрования пакетов
auth SHA1
cipher AES-256-CBC
; Максимальное число одновременно подключенных VPN-пользователей
max-clients 500
;client-config-dir /etc/mannaz/openvpn/ccd
; Задаем файл журналирования событий и уровень детализации
;журнала, отображаем не более 20 экземпляров одного сообщения (остальные
;отбрасываем)
;log-append openvpn.log
verb 4
mute 20
; Пользователь и группа, с правами которых работает демон
user nobody
group nogroup
; Эти опции предотвращают доступ к некоторым ресурсам
;(например, tun-устройству) при перезапуске демона (рекомендуется применять
;только при снижении привилегий)
#persist-key
#persist-tun
; Сажаем демона в chroot-окружение
#chroot /var/empty


auth-user-pass-verify /opt/vpn/openvpn/script/auth-user-pass-verify.php via-env
client-cert-not-required
username-as-common-name
;tmp-dir /etc/openvpn/tmp
#script-security 2

script-security 3
client-connect /opt/vpn/openvpn/script/connect.php
client-disconnect /opt/vpn/openvpn/script/logout.php

;Порт управления OpenVPN
management 0.0.0.0 7505



Я опишу только часть опций.

auth-user-pass-verify /opt/vpn/openvpn/script/auth-user-pass-verify.php via-env 
Эта опция нужна для того, чтоб авторизация работала по паролю. Суть такова: при подключении openvpn запускает этот внешний скрипт авторизации, если скрипт вернёт 0 по завершению, то авторизация пройдена успешна, если 1 - то провалена. В самом скрипте мы получаем переменную окружения
getenv("common_name")
имя пользователя и пароль
getenv("password")
, дальше делаем запрос в базу, проверяем, есть ли такой пользователь в базе с таким паролем. Если есть, то возвращаем 0, если нет - то 1. Вот как выглядит скрипт:

#!/usr/bin/php
<?php

//Подключаемся к базе

$link = mysqli_connect(

'localhost', /* Хост, к которому мы подключаемся */
'vpn', /* Имя пользователя */
'vpn', /* Используемый пароль */
'vpn'); /* База данных для запросов по умолчанию */

//Получаем переменные окружения, которые нам любезно предоставляет OpenVPN

$login = getenv("username"); // получаем логин
$pass = getenv("password"); // получаем пасс
$cn = getenv("common_name"); // получаем имя сертификата, под которым юзер пытается войти

//Выполняем запрос на поиск пользователя в базе
$result = mysqli_query($link, "SELECT
Users.`user`,
Users.`password`,
Users.`status`
FROM
Users
WHERE
Users.`user` LIKE '$login'");

$row = mysqli_fetch_assoc($result);

//Проверяем, соответствует ли пароль тот, который нам выдал ввёл клиент и тот, который  в базе; если всё хорошо - возвращаем 0 и выходим из скрипта,

if($row['password']==$pass)
{
exit(0);
}

//Если что-то пошло не так, то выходим и возвращаем 1, тем самым openvpn откажет клиенту в подключении
else{
exit(1);
}php?>


Написав этот скрипт не забудьте добавить права на исполнение. С авторизацией всё понятно, идём дальше. После удачной авторизации отслеживаем другое событие установки соединения. Вот так оно выглядит: в конфиге OpenVPN
client-connect /opt/vpn/openvpn/script/connect.php
опять вешаем скрипт. Сейчас объясню, зачем это. Всё дело в том, что в этом скрипте мы будем смотреть переменные окружения, а именно: имя пользователя, адрес клиента (ip из vpn сети). Итак, пишем такой скрипт:

#!/usr/bin/php
<?php
//Подключаем базу
include '/opt/vpn/www/db.php';
$cn = getenv("common_name"); // получаем имя сертификата, под которым юзер пытается войти
$ip=getenv("ifconfig_pool_remote_ip"); //Получаем ip адрес
$address = '127.0.0.1';//ip адрес сервевера
$port = 9090; // Порт 
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_connect($socket, $address, $port);

//Делаем запрос в базу для получения списка всех правил
$rules=mysqli_query($db,"SELECT RulesUsers.sport, RulesUsers.dport FROM RulesUsers WHERE RulesUsers.users LIKE '$cn'");
          {
                socket_write($socket, "addRuleIptables|$ip|".$rule['dport']."|".$rule['sport']."\r\n");
          }
socket_close($socket);
//}
?>


В этом скрипте используется сокет, объясняю почему. Всё дело в том, что OpenVPN мы запускаем не от root в целях безопасности, поэтому напрямую мы не можем добавить правило в iptables. Нужен промежуточный вариант, который можно сделать несколькими способами: это загрузка-выгрузка, то есть мы формируем в файле правила, дальше делаем другой скрипт который их загружает, можно использовать сокет суть в следующем. Пишем простое сервервер, на который мы будем слать команды из скрипта выполняемый OpenVPN, наш сервер будет запущен от root, получается, что команды будут моментально обрабатываться. Сервер я написал на perl, но на самом деле их много, на чём писать решать вам. Поскольку я все правила храню в MySQL, то когда OpenVPN дергает скрипты начинают слаться команды на сервер (сервер perl). Правило то мы добавили, теперь, когда пользователь отключается, то их нужно удалить. Вешаем другой скрипт
client-disconnect /opt/vpn/openvpn/script/logout.php
тут всё понятно - когда клиент отключается, openvpn дергает вот этот скрипт. Скрипт выглядит вот так:
#!/usr/bin/php
<?php
include '/opt/vpn/www/db.php';
$cn = getenv("common_name"); // получаем имя сертификата, под которым юзер пытается войти
$ip=getenv("ifconfig_pool_remote_ip"); //Получаем ip адрес
$address = '127.0.0.1';
$port = 9090;
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);

socket_connect($socket, $address, $port);
$rules=mysqli_query($db,"SELECT RulesUsers.sport, RulesUsers.dport FROM RulesUsers WHERE RulesUsers.users LIKE '$cn'");
while( $rule = mysqli_fetch_assoc($rules))
          {
                socket_write($socket, "delRuleIptables|$ip|".$rule['dport']."|".$rule['sport']."\r\n");
          }
socket_close($socket);
//}
?>


Тут всё то же самое, ищем правила в базе и посылаем их на сервер (сервер perl), где он их удаляет. Вообщем ничего нового. Забыл сказать, что все скрипты, которые дёргает Openvpn, могут быть написаны на чём угодно, только они должны иметь права на исполнение для того пользователя, от кого у вас запущен OpenVPN. И так теперь сам сервер, сделанный на Perl "server.pl". Этот скрипт нужно запустить от root, ниже приложен код срипта.

#!/usr/bin/perl
use Net::Server::PreFork;
@ISA = qw(Net::Server::PreFork);
sub process_request
{
        while (<STDIN />)
        {    # Net::Server дает нам сокет как STDIN + STDOUT!
        #print "Hello! \r\n";
        my @cmd=split('\|', $_);
                # socket_write($socket, "addRuleIptables|$ip|$dport|$sport|$cn\r\n");
                #Добавляем новое правило
                if ($cmd[0] eq 'addRuleIptables')
        {
                                system("/sbin/iptables -t nat -A vpn -i eth0 -p tcp -m tcp --dport $cmd[2] -j DNAT --to-destination $cmd[1]:$cmd[3]");
                                print "addRuleOK";

        }
                #Добавляем новое правило
                if ($cmd[0] eq 'delRuleIptables')
        {
                                #socket_write($socket, "delRuleIptables|$ip|$dport|$sport|$cn\r\n");
                                system("/sbin/iptables -t nat -D vpn -i eth0 -p tcp -m tcp --dport $cmd[2] -j DNAT --to-destination $cmd[1]:$cmd[3]");
                                print "delRuleOK";

        }
                #
        }
}
__PACKAGE__->run(port=>9090,user=>'root',group=>'root');


Тут всё просто, проверяем полученные данные на сокете, разбиваем строку на составляющие, дальше проверяем, какая пришла команда и выполняем условие на добавление правила или на удаление. Всё просто. Вот я кратко описал технологию, теперь осталось написать WEB интерфейс, что я и сделал. Написал все необходимые срипты, и сам web интерфейс, где заказчик сам делает правила. Выглядит это так.
Radmin за NAT (Менеджер переадрисации портов)


Инструкция по установке

Распаковываем архив
Итак, папку www кидаем туда, откуда веб сервер берёт страницы, у меня это /var/www
Открываем файл "db.php", редактируем параметры подключения в MySQL, правим под свои параметры
Создаём базу данных в MySQL с таблицами, заходим в phpmyadmin, выбираем "создать базу", после выбираем эту базу и импортируем туда таблицы с файла "vpn.sql"
Создаём сертификаты и ключи для сервера OpenVPN; для пользователей не нужно создавать отдельных сертификатов
Создаём файл конфигурации OpenVPN, у меня он называется "server.conf"
Кладём скрипты "auth-user-pass-verify.php"," connect.php", "logout.php" куда угодно, только не забудем это сказать OpenVPN, укажите в файле конфигурации "server.conf", все 3 файла нужно отредактировать, вписать свой путь к
include '/opt/vpn/www/db.php

Дальше копируем сервер комманд "server.pl", если нужно исправить настройки, то правим
port=>9090,user=>'root',group=>'root'
если меняем порт, то во всех скриптах его меняем, выставляем на нём права на исполнение и запускаем от root.
./server.pl &
ну или так:
./server.pl >server.log &

запускаем openvpn, "openvpn -config server.conf"
Включаем транзитный трафик
echo "1" > /proc/sys/net/ipv4/ip_forward
, создаём отдельную цепочку
iptables -t nat -N vpn,
добавляем правило подмены ip
iptables -t nat -A POSTROUTING -d 192.168.22.0/23 -o tun0 -j SNAT --to-source 192.168.22.1
, и добавляем правило запрета транзитного трафика между клиентами OpenVPN
iptables -A FORWARD -i tun0 -o tun0 -j DROP
; добавляем правило, разрешающее трафик "icmp" банально, чтобы можно было делать пинг до сервера от клиентов через туннель
iptables -A INPUT -p icmp -j ACCEPT
, добавляем правило на запрет, чтобы клиенты не могли по туннелю подключиться к серверу по другим протоколам
iptables -A INPUT -i tun0 -j DROP
. Я сделал крип "setupiptables" , со всеми правилами также прилагается в архив.
Добавляем всё в автозагрузку в
nano /etc/rc.local
, у меня выглядит так:
/opt/vpn/server/server.pl &
/usr/sbin/openvpn --config /opt/vpn/openvpn/server.conf
/opt/vpn/openvpn/setupiptables

Веб интерфейсе реализовано:
Управление пользователями, управление правилами, отслеживание подключенных пользователей в реальном времени, выгрузка всех подключений в формат адресной книги radmin
После настройки, заходим в веб интерфейс:
1. Добавляем пользователя
2. Добавляем правила для пользователя
Для подключения клиента нужно установить OpenVPN, скопировать файл конфигурации и сертификат (он общий для всех), вписать пароль в файл, после этого можно пробовать подключаться.

Для настройки клиента нужно скопировать
ca.crt 
Сертификат нужно брать с сервера
pass.txt
Тут нужно вписать логин и пароль
client.ovpn
Поправить ip адрес
Скриншоты веб интерфейса
Пользователи
Radmin за NAT (Менеджер переадрисации портов)

Правила для пользователя
Radmin за NAT (Менеджер переадрисации портов)

Пользователи, которые сейчас подключены
Radmin за NAT (Менеджер переадрисации портов)

Выгрузка правил из группы radmin на адресную книгу radmin, которую можно подгрузить radmin
Radmin за NAT (Менеджер переадрисации портов)

Ну на этом всё, внизу прилагаю архив. Данный проект пока находится на стадии Бета-тестирования, все ошибки будут исправляться по мере их появления. Как всегда вопросы приветствуются, пишите в комментариях, так же могу помочь с установкой данного проекта на сервер. Пишем всё в комментариях
v1.0 bскачать dle 11.3
Информация
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.