05 сентября 2014

Шлюз для VPN на Freebsd

Данная статья является руководством, как организовать VPN-соединение, используя протоколы pptp, l2tp и настроить общение сервера с клиентом на Freebsd .

Многие из вас, конечно, знают о пользе VPN (Virtual Private Network). Эту технологию используют всякий раз, когда требуется связать удаленные подсети через Интернет так, как будто бы они находились рядом, в одной локальной сети.

Еще она полезна тем, что обеспечивает безопасный канал данных. А этот вопрос сейчас актуален, как никогда. Мы повсюду пользуемся открытыми wi-fi сетями, в офисах, отелях или кафе. И кто сможет гарантировать нам, что какой-нибудь злоумышленник не сможет перехватить наши персональные данные? Поэтому используйте vpn-клиенты и на ваших мобильных устройствах.

Приступая к вопросу создания vpn-сервера надо сказать, что единого стандарта VPN не существует. Ее можно построить с использованием разных протоколов. Самым распространенным по сей день остается PPTP (Point to Point Tunneling Protocol), в нем используется алгоритм шифрования MPPE (Microsoft Point-to-Point Encryption) с 40, 56 и 128 битным шифрованием. Для проверки аутентификации применяются MS-CHAPv2 или EAP-TLS. Но в этих алгоритмах были обнаружены серьёзные уязвимости. Поэтому использование pptp с точки зрения безопасности крайне не желательно!

Усовершенствованным протоколом является L2TP (Layer 2 Tunneling Protocol). Это совместное детище Microsoft и Cisco, комбинация PPTP и L2F. Но в отличие от PPTP для шифрования PPP-датаграмм здесь используется IPsec. Хотя l2tp  хорошо работает и без шифрования. Имейте в виду, что в windows по умолчанию оно используется! Что бы отключить, правим в реестре

REGEDIT4
[HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Rasman\Parameters]
«ProhibitIpSec»=dword:00000001

И так, перейдем ближе к делу.
На VPN-сервере мы будем использовать демон mpd5 (Netgraph multi-link PPP daemon). Он крайне надежен, не требователен к ресурсам и обладает высокой производительностью, на современном оборудовании может справиться с несколькими гигабит PPP-трафика в секунду.
Он манипулирует подсистемой Netgraph и если ее нет в ядре (а по умолчанию ее нет), то сам заботливо подключает все необходимые модули. Проверить какие модули подключаются можно после запуска mpd командой 

# kldstat

Для начала мы потренируемся и настроим PPTP. Это очень быстро и просто!

Дополнительно у него есть механизм сжатия данных, но что бы им воспользоваться, нужно перекомпилировать модуль ng_mppc.ko. По умолчанию он собран без поддержки компрессии! Но сперва хорошенько подумайте, оно вам надо? Как показала практика, без нее vpn работает быстрее и стабильнее.
Если надо, то придется дополнительно скачать проприетарную библиотеку MPPC. Взять ее можно на сайте Alternative MPPC compression/decompression library.
# fetch http://mavhome.dp.ua/MPPC/mppc-1.0.tgz
# tar -xvf mppc-1.0.tgz -C /usr/src/sys/net/
Далее в файле /usr/src/sys/modules/netgraph/mppc/Makefile поправьте
NETGRAPH_MPPC_COMPRESSION?= 1
И соберите модуль
# cd /usr/src/sys/modules/netgraph/mppc
# make && make install && make clean
Так же компрессию для PPTP можно включить прямо в ядро. Но я буду собирать его без компрессии и только с нужными опциями netgraph. Сборка займет около 40 минут, в зависимости от мощности вашего компьютера.
options NETGRAPH 
options NETGRAPH_ETHER 
options NETGRAPH_SOCKET 
options NETGRAPH_TEE 
options NETGRAPH_MPPC_ENCRYPTION 
# options NETGRAPH_MPPC_COMPRESSION
options NETGRAPH_BPF 
options NETGRAPH_IFACE 
options NETGRAPH_KSOCKET 
options NETGRAPH_PPP 
options NETGRAPH_PPTPGRE 
options NETGRAPH_TCPMSS 
options NETGRAPH_VJC 
options NETGRAPH_ONE2MANY 
options NETGRAPH_RFC1490 
options NETGRAPH_TTY 
options NETGRAPH_UI
options NETGRAPH_L2TP
Копирую конфиг ядра из шаблона, добавляю опции, собираю и устанавливаю.
# cd /usr/src/sys/amd64/conf
# cp GENERIC mykernel
# cd /usr/src
# vi mykernel
# make buildkernel KERNCONF=mykernel
# make installkernel KERNCONF=mykernel
После перезагрузки проверяем, что новое ядро успешно загрузилось командой uname -a

Теперь ставим mpd5 из пакета
# pkg install mpd5
или из порта
# portsnap fetch update
# cd /usr/ports/net/mpd5
# make config install clean
Возьмем за основу файл конфигурации из шаблона
# cd /usr/local/etc/mpd5
# cp mpd.conf.sample mpd.conf
Для примера, представим, что у нас есть офисная подсеть 192.168.10.0/24, в ней имеется шлюз  с внутренним адресом 192.168.10.1 и внешним 1.2.3.4, на котором запущен mpd.

Тогда согласно этой топологии отредактируем mpd.conf. Для настройки pptp находим в нем секцию pptp_server и меняем там только такие строчки, остальные оставляем без изменения:
# задаем логин к web-интерфейсу
set user username pass admin
# комментируем эту строчку за ненадобностью
#set user foo1 bar1
# по умолчанию прописываем старт pptd-сервера
default:
load pptp_server
pptp_server:
# пул адресов
set ippool add pool1 192.168.10.200 192.168.10.220
# указываем mpd-сервер и назначаем для подсети пул
set ipcp ranges 192.168.10.1/32 ippool pool1
# указываем dns провайдера или свой
set ipcp dns 192.168.10.1
# если нет WINS, то комментируем
#set ipcp nbns 192.168.1.4
# принимать PPTP подключения на всех интерфейсах или указываем конкретный
set pptp self 0.0.0.0
Если ваш сервер выполняет роль маршрутизатора и раздает интернет при помощи NAT, то  клиенты подключившиеся через vpn так же смогут выходить в интернет.  Правильно укажите адреса dns в mpd.conf. Используйте те, которые выдал провайдер, либо ставьте локальный кеширующий dns, например bind.

Создаем файл с логинами и паролями
# touch mpd.secret
Добавляем в него пользователей
#login password ip
user1 pass1 192.168.10.193
user2 pass2 
Mpd слушает 1723 TCP-порт и после удачной аутентификации с помощью протокола GRE устанавливается PPP-сессия, при этом создаются виртуальные ng* интерфейсы. Поэтому в фаервол нужно добавить правила разрешающие gre по всем интерфейсам и 1723 TCP-порт для управления соединением.   
allow tcp from any to me 1723
allow gre from any to any
Настроим логирование mpd  в файл mpd.log. Для этого в конец /etc/syslog.conf добавим строчки
!mpd 
*.* /var/log/mpd.log 
И еще не забудем про ротацию. В /etc/newsyslog.conf добавим
/var/log/mpd.log 600 7 * @T00 JC
Создадим лог-файл и перезапустим сервис
# touch /var/log/mpd.log
# service syslogd reload
или так /etc/rc.d/syslogd reload Это одно и тоже.

Для автозапуска добавим в /etc/rc.conf параметр
mpd_enable="YES"
mpd_flags="-b"
И запускаем mpd5
# service mpd5 start
Проверяем, слушается ли порт:
# netstat -an | grep 1723
Подключаемся любым клиентом, радуемся! 
Так же для проверки заходим в веб-админку http://server:5006 и смотрим параметры.

mpd5 pptp-client
Что бы настроить mpd, как клиента pptp, в конфигурацонном файле mpd.conf должна быть секция (взято из шаблона).
default:
load pptp_client
pptp_client:

create bundle static B1

# одно из двух, либо это 
set iface route default
set ipcp ranges 0.0.0.0/0 0.0.0.0/0 
# либо это 
#set iface up-script /usr/local/etc/mpd5/up.shset 
#set iface down-script /usr/local/etc/mpd5/down.sh

create link static L1 pptp
set link action bundle B1
set auth authname login_name
set auth password password
set link max-redial 0
set link mtu 1460
set link keep-alive 20 75
set pptp peer ip_pptp_server
set pptp disable windowing
open
И правила для фаерволов. Со стороны сервера 1723 порт, на клиенте произвольный больше 1000. И для установления соединения протокол gre.
 
Еще хочу дать небольшое пояснение. 
Предположим, что соединение происходит по следующей схеме.


После того, как ваш pptp-клиент подключится к серверу, он присоединиться к его локальной сети и будет иметь доступ ко всем подсетям, которые маршрутизирует сервер. Кроме того, если сервер имеет NAT, то вы так же получите доступ к сети интернет через его интерфейс, если пропишете на клиенте в качестве шлюза внутренний адрес pptp-сервера.
route delete default
route add default 192.168.10.1
Но при этом нужно сохранить прежний маршрут для поддержания pptp-канала через основной шлюз, которым вы пользовались по умолчанию, в нашем случае это, например, 1.2.3.1
route add 1.2.3.5 1.2.3.1
Но локальные сети pptp-клиента и сервера по прежнему не имеет доступа друг к другу, поскольку pptp-сервер ничего о них не знает. Для этого на сервере вручную нужно указать  маршрут до сетей клиента. 
route add -net 192.168.20.0/24 192.168.10.5
Теперь все сети видят друг друга, а сети клиента даже могут выходить в интернет через pptp-сервер.

Что бы эти маршруты создавались автоматический можно оформить их в виде отдельных скриптов и прописать в mpd.conf.
set iface up-script /usr/local/etc/mpd5/up.sh
set iface down-script /usr/local/etc/mpd5/down.sh
Они будут срабатывать при установлении соединения. Важный момент, в каком месте mpd.conf их расположить. Если это клиент, то правильно в начале секции заместо "set iface route default", тогда ее нужно закомментировать (см. пример выше), иначе не запускаются. Если сервер, то так же в начале, после "create bundle template BL".

Пример up.sh
#!/bin/sh
route delete default
route add 1.2.3.5 1.2.3.1
route add default 192.168.10.1
В down.sh надо все вернуть обратно как было.
#!/bin/sh
route delete default
route delete
1.2.3.5 1.2.3.1
route add default 192.168.15.1


Настройка l2tp на mpd выполняется по аналогии с pptp. В конфиг mpd.conf нужно добавить секцию l2tp_server с точно такими же параметрами, где все буквосочетания pptp заменяются на l2tp. Жирным выделено то, чем отличается от шаблона.
default:
load pptp_server
load l2tp_server
l2tp_server:
# Define dynamic IP address pool.
set ippool add pool1 192.168.10.200 192.168.10.220
# Create clonable bundle template named B
create bundle template BB
set iface enable proxy-arp
set iface idle 1800
set iface enable tcpmssfix
set ipcp yes vjcomp
# Specify IP address pool for dynamic assigment.
set ipcp ranges 192.168.10.1/32 ippool pool1
set ipcp dns 192.168.10.1
#set ipcp nbns 192.168.1.4
# The five lines below enable Microsoft Point-to-Point encryption
# (MPPE) using the ng_mppc(8) netgraph node type.
set bundle enable compression
set ccp yes mppc
set mppc yes e40
set mppc yes e128
set mppc yes stateless
# Create clonable link template named L
create link template LL l2tp
# Set bundle template to use
set link action bundle BB
# Multilink adds some overhead, but gives full 1500 MTU.
set link enable multilink
set link yes acfcomp protocomp
set link no pap chap eap
set link enable chap-msv2
# We can use use RADIUS authentication/accounting by including
# another config section with label ‘radius’.
# load radius
set link keep-alive 10 60
# We reducing link mtu to avoid GRE packet fragmentation.
set link mtu 1460
# Configure PPTP
set l2tp self 0.0.0.0
# Allow to accept calls
set link enable incoming
Перезапускаем mpd и проверяем, что у нас слушается 1701 udp порт.
# netstat -an | grep 1701
Далее добавляем поддержку IPsec и пересобрать ядро.
# ядерная поддержка IP security
options IPSEC
# изменение и восстановление заголовков пакетов за NAT
options IPSEC_NAT_T
# поддержка криптографии
device crypto
Начиная с версии 11.0 IPSEC_NAT_T включен в ядро по умолчанию, поэтому ее можно закомментировать!

После того как вы установите IPsec, его надо настроить. Вам нужно будет указать какой трафик и как следует шифровать. Для этого в IPsec есть специальная база SPD (Security Policy Database), где хранятся политики безопасности. Ее можно заполнить статически, скриптом setkey.conf, который будет срабатывать при инициализации системы. Для этого в rc.conf нужно добавить строчки
ipsec_enable=”YES”
ipsec_program=”/usr/local/sbin/setkey”
ipsec_file=”/usr/local/etc/racoon/setkey.conf”
IPsec может шифровать любой трафик, но нам надо только канал l2tp. Весь шифрованный трафик будет передаваться по протоколу esp. И есть два варианта настройки, транспортный режим или туннельный. В первом случае в ip-пакет встраиваются только шифрованные данные, а заголовок остается неизменным и содержит оригинальные ip. Во втором, изменяется ip-заголовок и в шифрованную часть esp-кадра помимо данных еще встраиваются оригинальные адреса источника и отправителя. Таким образом скрывются реальные ip-адреса внутренних подсетей и обеспечивается их безопасность. Но естественно это требует дополнительных вычислительных мощностей.

Поэтому, если вы организуете vpn между двумя подсетями и НЕ хотите, чтобы в Интернете курсировали их ip-пакеты, то надо настраивать туннельный режим.  А если у вас подключение хост-хост, то только транспортный!

Для наглядности хочу приложить датаграмму, поясняющую отличия транспортного режима от туннельного.


Настройки для шифрования трафика между сервером и произвольным узлом будут выглядят следующим образом. В файле /usr/local/etc/racoon/setkey.conf
flush;
spdflush;
spdadd 0.0.0.0/0[0] 1.2.3.4/0[1701] udp -P in ipsec esp/transport//require;
spdadd 1.2.3.4/0[1701] 0.0.0.0/0[0] udp -P out ipsec esp/transport//require;
А между двумя подсетями, например, 10.246.38.0/24 и 10.0.0.0/24, в каждой есть свой шлюз  с внешним адресом 172.16.5.4 и 192.168.1.12.
spdadd 10.246.38.0/24 10.0.0.0/24 any -P out ipsec esp/tunnel/172.16.5.4-192.168.1.12/require;
spdadd 10.0.0.0/24 10.246.38.0/24 any -P in ipsec esp/tunnel/192.168.1.12-172.16.5.4/require;
После того, как настроете политики безопасности, посмотрите, как они будут выгляднть до и после подключений клиентов, это делается командой
# setkey -DP
Настройка Racoon
Далее клиенту и серверу нужно договорится о алгоритме и ключах шифрования. Это делается с помощью специального демона по протоколу IKE (Internet Key Exchange protocol). Мы будем использовать Racoon из порта security/ipsec-tools.  

Но перед установкой мы внесем в код небольшие изменения, что бы можно было подключаться с динамических ip-адресов, которые заранее не известны.
# cd /usr/ports/security/ipsec-tools
# make fetch
# make checksum
# make depends
# make extract
# cd /usr/ports/security/ipsec-tools/work/ipsec-tools-0.8.1/src/racoon
Там открываем файл localconf.c и находим функцию getpsk(str, len). В ней строку
if (strncmp(buf, str, len) == 0 && buf[len] == '\0') {
заменяем на
if(strcmp(buf, "*")==0||(strncmp(buf,str,len)==0&& buf[len]=='\0')){
И еще в файле ipsec_doi.c находим функцию ipsecdoi_checkid1(iph1), в ней условный оператор
if (iph1->etype == ISAKMP_ETYPE_IDENT &&
iph1->approval->authmethod == OAKLEY_ATTR_AUTH_METHOD_PSKEY)
{ if (id_b->type != IPSECDOI_ID_IPV4_ADDR &&
id_b->type != IPSECDOI_ID_IPV6_ADDR)
{ plog(LLV_ERROR, LOCATION, NULL,
"Expecting IP address type in main mode, "
"but %s.\n", s_ipsecdoi_ident(id_b->type));
return ISAKMP_NTYPE_INVALID_ID_INFORMATION;
} }
 
Меняем здесь LLV_ERROR на LLV_WARNING и комментируем return используя /* */.
Сохраняем, собираем и устанавливаем порт.
# cd /usr/ports/security/ipsec-tools
# make build
# make install clean
Теперь настроим файл конфигурации recoon. Возьмем из шаблона и скопируем его в каталог /usr/local/etc/racoon
# cp /usr/local/share/examples/ipsec-tools/racoon.conf.sample /usr/local/etc/racoon/racoon.conf
Редактируем, он должен содержать следующие строчки. 
path include "/usr/local/etc/racoon";
path pre_shared_key "/usr/local/etc/racoon/psk.txt";
log info;
listen {
        isakmp 1.2.3.4 [500];
        isakmp_natt 1.2.3.4 [4500];
}
remote anonymous {
                  exchange_mode main;
                  proposal_check obey;
                  lifetime time 24 hour;
                  generate_policy on;
                  nat_traversal on;
proposal {
          encryption_algorithm 3des;
          hash_algorithm sha1;
          authentication_method pre_shared_key;
          dh_group 2;
}}
sainfo anonymous {
                  lifetime time 1 hour ;
                  encryption_algorithm aes;
               authentication_algorithm hmac_sha1, hmac_md5;
                  compression_algorithm deflate;
}
Справку по всем опциям можете смотреть здесь.

Обратите внимание, что в конфигурационном файле racoon.conf есть опция generate_policy on. Благодаря ей демон динамически генерирует политики безопасности в соответствии с нуждами клиента и обновляет базу SPD.

Благодаря этому особой нужды в setkey.conf  нет. Но в процессе эксплуатации выяснилось, что если клиент находится за NAT, то по каким то причинам одних динамических политик не достаточно и он не может подключиться. Если без NAT, то все  работает на ура.

Поэтому специально для таких клиентов обязательно настройте статические правила безопасности SPD, как было описано выше.

Аутентификация в IPsec может происходить по паролю или сертификату. Сейчас мы будем использовать пароли, только здесь они называются открытыми ключами. Они должны находится в файле /usr/local/etc/racoon/psk.txt и быть в формате "ip  пароль" для каждого клиента отдельно.
Но благодаря тому, что мы внесли изменения в код, теперь у нас задается один пароль для всех подключений!
* password
Так же для этого файла нужно задать права доступа
# chmod 600  /usr/local/etc/racoon/psk.txt
Для записи логов racoon добавляем в /etc/syslog.conf строчку
!racoon 
*.* /var/log/racoon.log
И прописываем в rc.conf автозагрузку
racoon_enable="YES"
racoon_flags=”-l /var/log/racoon.log”
Еще не забудьте добавить правила для фаервола, разрешающие IKE трафик, это порты 500 и 4500 UDP.
allow udp from me isakmp to any
allow udp from any to me isakmp
allow udp from any to me 4500
allow udp from me 4500 to any
Так же потребуются правила для ESP и IPENCAP в обе стороны. 
allow esp from me to any
allow esp from any to me
allow ipencap from me to any
allow ipencap from any to me
 Все настроили, поздравляю! Теперь для теста запускаем racoon в фоновом режиме
# /usr/local/sbin/racoon -F -f /usr/local/etc/racoon/racoon.conf
У вас должно появиться следующее сообщение приветствия
Foreground mode.2014-09-06 09:49:44: INFO: @(#)ipsec-tools 0.8.1 (http://ipsec-tools.sourceforge.net)
2014-09-06 09:49:44: INFO: @(#)This product linked OpenSSL 1.0.1e-freebsd 11 Feb 2013 (http://www.openssl.org/)
2014-09-06 09:49:44: INFO: Reading configuration from "/usr/local/etc/racoon/racoon.conf"
2014-09-06 09:49:44: INFO: 1.2.3.4[4500] used for NAT-T
2014-09-06 09:49:44: INFO:
1.2.3.4[4500] used as isakmp port (fd=5)
2014-09-06 09:49:44: INFO:
1.2.3.4[500] used for NAT-T
2014-09-06 09:49:44: INFO:
1.2.3.4[500] used as isakmp port (fd=6)
Это означает, что демон начал слушать. Теперь подключаетесь клиентом и смотрите какие сообщения он будет вадавать.

Что бы настроить racoon в качетве клиента потребуется следующий конфиг
path pre_shared_key "/usr/local/etc/racoon/psk.txt";
log info;

listen {
        isakmp client_ip;
        strict_address;
}

remote server_ip {
        exchange_mode main;
        lifetime time 24 hour;
        my_identifier address;
        peers_identifier address;
        passive off;
        generate_policy off;
        proposal {
                encryption_algorithm 3des;
                hash_algorithm sha1;
                authentication_method pre_shared_key;
                dh_group 2;
        }
}

sainfo anonymous {
        encryption_algorithm aes;
        authentication_algorithm hmac_sha1, hmac_md5;
        lifetime time 1 hour ;
        compression_algorithm deflate;
}
И в файле psk.txt должен быть такой же ключ, как на сервере.
server_ip password
Так же нужны политики безоппасности для определения какой трафик шифровать.
flush; 
spdflush; 
spdadd client_ip[0] server_ip[1701] udp -P out ipsec esp/transport//require; 
spdadd server_ip[1701] client_ip[0] udp -P in ipsec esp/transport//require;
На стороне сервера политики можно не определять, достаточно опции generate_policy on;
Пробуйте подключиться
racoonctl vpn-connect 192.168.15.1

Если все пройдет удачно, то по команде
# setkey -D
увидите установленные соединения
192.168.15.2 192.168.15.1
        esp mode=transport spi=237603062(0x0e2988f6) reqid=0(0x00000000)
        E: rijndael-cbc  c63f1e8a ec2e5696 b4d11f49 43808d67
        A: hmac-sha1  216134ff 88b7f5e4 2ba8636c 6bce456c bcc5be04
        seq=0x0000002a replay=4 flags=0x00000000 state=mature
        created: Nov 22 09:48:53 2014   current: Nov 22 09:54:44 2014
        diff: 351(s)    hard: 3600(s)   soft: 2880(s)
        last: Nov 22 09:54:40 2014      hard: 0(s)      soft: 0(s)
        current: 4176(bytes)    hard: 0(bytes)  soft: 0(bytes)
        allocated: 42   hard: 0 soft: 0
        sadb_seq=1 pid=6713 refcnt=2
192.168.15.1 192.168.15.2
        esp mode=transport spi=143240586(0x0889ad8a) reqid=0(0x00000000)
        E: rijndael-cbc  677283ac fe29513a 9a2718c9 4ce0cd7f
        A: hmac-sha1  ce28280f c819ae0e 99601510 bb99bda8 767bfefe
        seq=0x00000028 replay=4 flags=0x00000000 state=mature
        created: Nov 22 09:48:53 2014   current: Nov 22 09:54:44 2014
        diff: 351(s)    hard: 3600(s)   soft: 2880(s)
        last: Nov 22 09:54:40 2014      hard: 0(s)      soft: 0(s)
        current: 2154(bytes)    hard: 0(bytes)  soft: 0(bytes)
        allocated: 40   hard: 0 soft: 0
        sadb_seq=0 pid=6713 refcnt=1
Если что то пойдет не так, смотрите в логи /var/log/racoon.log на клиенте и сервере, там сразу все сразу станет ясно!
Желаю успехов!

2 комментария:

  1. Для примера, представим, что у нас есть офисная подсеть 192.168.10.0/32
    Интересная маска подсети 32. Вы часом не ошиблись?

    ОтветитьУдалить
    Ответы
    1. Да, спасибо, исправил! Че в ней интересного, обычная маска, только задающая конкретный хост. Видимо на автомате взял из конфига и забыл изменить.

      Удалить