Apps: "strongswan", "xl2tpd", "ip", "netplan".
Задача: наладить подключение linux-системы к L2TP-серверу, с обязательным шифрованием VPN-трафика.
Для справки: L2TP ("Layer 2 Tunneling Protocol") представляет собой сетевой протокол туннелирования канального уровня, сочетающий в себе протокол L2F ("Layer 2 Forwarding"), разработанный компанией "Cisco", и протокол PPTP корпорации "Microsoft". Позволяет организовывать VPN с заданными приоритетами доступа, однако не содержит в себе средств шифрования и механизмов аутентификации - потому для создания защищённой VPN его используют совместно с IPSec.
Для управления L2TP-соединениями воспользуемся подсистемой "xl2tpd", а для шифрования - "strongSwan".
Последовательность действий такова:
1. Конфигурирование подсистемы IPsec-шифрования;
2. Конфигурирование подсистемы L2TP-подключений;
3. Автоматизация добавления сетевых маршрутов;
4. Ручное управление VPN-соединениями;
5. Автоматизация инициализации VPN-соединений.
2. Конфигурирование подсистемы L2TP-подключений;
3. Автоматизация добавления сетевых маршрутов;
4. Ручное управление VPN-соединениями;
5. Автоматизация инициализации VPN-соединений.
Конфигурирование подсистемы IPsec-шифрования.
Инсталлируем необходимые приложения и утилиты:
# apt-get -y install strongswan
У проекта "strongSwan" хорошая документация, из которой для решения нашей простой задачи вполне достаточно чтения пары страниц: "ConnSection" и "IpsecCommand".
В основном конфигурационном файле подсистемы IPsec-шифрования "strongSwan" описываем общие параметры и правила шифрования трафика от IP до IP (пример связи с L2TP/типовым IPsec-шлюзом на базе "RouterOS"):
# vi /etc/ipsec.conf
# ipsec.conf - strongSwan IPsec configuration file
# Basic configuration
config setup
# strictcrlpolicy=yes
# uniqueids = no
# nat_traversal = yes
# protostack = netkey
# Default options for encryption of connections
conn %default
ikelifetime = 60m
keylife = 20m
rekeymargin = 3m
keyingtries = 1
keyexchange = ikev1
authby = secret
ike = aes128-sha1-modp2048!
esp = aes128-sha1-modp2048!
# Encryption of traffic to the "l2tp.example.net"
conn ipsec-gate.example.net
# Automatic start of encryption at system startup
auto = start
# Restarting encryption if negotiation fails
dpdaction = restart
# Restart encryption on unexpected close
closeaction = restart
# Reconnect unlimited number of times
keyingtries = %forever
keyexchange = ikev1
pfs = no
authby = secret
type = transport
left = %defaultroute
leftprotoport = 17/1701
right = 100.200.250.21
rightprotoport = 17/1701
include /var/lib/strongswan/ipsec.conf.inc
# Basic configuration
config setup
# strictcrlpolicy=yes
# uniqueids = no
# nat_traversal = yes
# protostack = netkey
# Default options for encryption of connections
conn %default
ikelifetime = 60m
keylife = 20m
rekeymargin = 3m
keyingtries = 1
keyexchange = ikev1
authby = secret
ike = aes128-sha1-modp2048!
esp = aes128-sha1-modp2048!
# Encryption of traffic to the "l2tp.example.net"
conn ipsec-gate.example.net
# Automatic start of encryption at system startup
auto = start
# Restarting encryption if negotiation fails
dpdaction = restart
# Restart encryption on unexpected close
closeaction = restart
# Reconnect unlimited number of times
keyingtries = %forever
keyexchange = ikev1
pfs = no
authby = secret
type = transport
left = %defaultroute
leftprotoport = 17/1701
right = 100.200.250.21
rightprotoport = 17/1701
include /var/lib/strongswan/ipsec.conf.inc
Секретный ключ шифрования PSK ("Pre-Shared Key"), используемый для предварительной клиент-серверной аутентификации, выносится в отдельный файл, где указывается, в каких условиях таковой должен применяться:
# vi /etc/ipsec.secrets
# <GATEWAY IP ADDRESS> <SERVER ADDRESS> : PSK "<PRE-SHARED KEY>"
%any 100.200.250.21 : PSK "strongSecretKey"
%any 100.200.250.21 : PSK "strongSecretKey"
Защищаем файл с паролями от доступа посторонних:
# chown root /etc/ipsec.secrets
# chmod 600 /etc/ipsec.secrets
# chmod 600 /etc/ipsec.secrets
Для применения изменений в конфигурации перезапускаем сервис подсистемы IPSec-шифрования:
# systemctl restart strongswan
# systemctl status strongswan
# systemctl status strongswan
Если конфигурация IPsec выше подразумевает автоматическое включение шифрования, то сразу после перезапуска "strongSwan" мы уже должны получить желаемое.
Если режим запуска шифрования для какого-то направления выбран "ручной" (auto=add), то запускаем его соответствующей командой:
# ipsec up ipsec-gate.example.net
Соответственно, при необходимости, аналогичной ручному запуску командой можно шифрование в заданном направлении остановить:
# ipsec down ipsec-gate.example.net
Просматриваем статус IPsec-соединений:
# ipsec statusall
# ipsec status
# ipsec status
Security Associations (1 up, 0 connecting):
ipsec-gate.example.net[1]: ESTABLISHED 43 seconds ago, 10.20.30.45...100.200.250.21
ipsec-gate.example.net{1}: INSTALLED, TRANSPORT, reqid 1, ESP in UDP SPIs: c57b51bb_i 04fa2652_o
ipsec-gate.example.net{1}: 10.20.30.45/32[udp/l2f] === 100.200.250.21/32[udp/l2f]
ipsec-gate.example.net[1]: ESTABLISHED 43 seconds ago, 10.20.30.45...100.200.250.21
ipsec-gate.example.net{1}: INSTALLED, TRANSPORT, reqid 1, ESP in UDP SPIs: c57b51bb_i 04fa2652_o
ipsec-gate.example.net{1}: 10.20.30.45/32[udp/l2f] === 100.200.250.21/32[udp/l2f]
Теперь весь трафик к целевому сетевому узлу инкапсулируется в IPsec.
Конфигурирование подсистемы L2TP-подключений.
Инсталлируем необходимые приложения и утилиты:
# apt-get -y install xl2tpd net-tools
Параметры для настройки L2TP-подключения посредством "xl2tpd" хорошо описаны во встроенном руководстве по эксплуатации, вызываемом командой "man xl2tpd.conf" (как вариант, можно посмотреть web-версию).
Описываем параметры VPN-канала, который должен быть установлен посредством "xl2tpd":
# vi /etc/xl2tpd/xl2tpd.conf
[lac l2tp-gate.example.net]
lns = 100.200.250.21
;ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.client-gate.example.net
length bit = yes
autodial = yes
redial = yes
redial timeout = 30
lns = 100.200.250.21
;ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd.client-gate.example.net
length bit = yes
autodial = yes
redial = yes
redial timeout = 30
Описываем параметры PPP-соединения, включая логин и пароль для аутентификации:
# vi /etc/ppp/options.l2tpd.client-gate.example.net
ipcp-accept-local
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
mtu 1280
mru 1280
noipdefault
nodefaultroute
usepeerdns
connect-delay 5000
name vpn-node0-tunnel
password strongSecretPassword
ipcp-accept-remote
refuse-eap
require-mschap-v2
noccp
noauth
mtu 1280
mru 1280
noipdefault
nodefaultroute
usepeerdns
connect-delay 5000
name vpn-node0-tunnel
password strongSecretPassword
Защищаем файл с паролем от доступа посторонних:
# chmod 600 /etc/ppp/options.l2tpd.client-gate.example.net
Для применения изменений в конфигурации перезапускаем сервис:
# systemctl restart xl2tpd
# systemctl status xl2tpd
# systemctl status xl2tpd
Если конфигурация "xl2tpd" выше подразумевает автоматическое создание VPN-туннеля, то сразу после перезапуска "xl2tpd" мы уже должны получить желаемое.
Если режим запуска создания VPN-туннеля выбран "ручной" (autodial=no), то запускаем его соответствующей командой:
# xl2tpd-control connect l2tp-gate.example.net
Соответственно, при необходимости, аналогичной ручному режиму командой можно определённый VPN-туннель выключить и удалить виртуальный интерфейс:
# xl2tpd-control disconnect l2tp-gate.example.net
# xl2tpd-control remove l2tp-gate.example.net
# xl2tpd-control remove l2tp-gate.example.net
После вышеприведённых действий в перечне сетевых интерфейсов должен появится новый виртуальный PPP-интерфейс, через который осуществляется передача трафика до целевого L2TP-сервера:
# ifconfig -a
....
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1280
inet 10.172.255.2 netmask 255.255.255.255 destination 10.172.255.1
ppp txqueuelen 3 (Point-to-Point Protocol)
....
ppp0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1280
inet 10.172.255.2 netmask 255.255.255.255 destination 10.172.255.1
ppp txqueuelen 3 (Point-to-Point Protocol)
....
Простейшим способом проверяем доступность опорного IP-адреса на стороне L2TP-сервера:
# ping 10.172.255.1
Слегка напрягает, что сервисы, обеспечивающие работу IPsec и L2TP прослушивают ряд портов в ожидании входящих подключений тогда, как нам требуется от них только работа в роли клиентов - но, насколько я понимаю, это неотъемлемая особенность работы подсистем, обеспечивающих двухсторонние сетевые туннели:
# netstat -apn
Proto ... Local Address Foreign Address PID/Program name
udp ... 0.0.0.0:4500 0.0.0.0:* .../charon
udp ... 0.0.0.0:500 0.0.0.0:* .../charon
udp ... 0.0.0.0:1701 0.0.0.0:* .../xl2tpd
....
udp ... 0.0.0.0:4500 0.0.0.0:* .../charon
udp ... 0.0.0.0:500 0.0.0.0:* .../charon
udp ... 0.0.0.0:1701 0.0.0.0:* .../xl2tpd
....
Разумеется, можно прикрыть сетевые сервисы защитным экраном "iptables", предписав предоставлять доступ к портам только для запросов со стороны L2TP-сервера.
Автоматизация добавления сетевых маршрутов.
В производственной среде VPN-туннели чаще предназначены для передачи только выделенного трафика - к тем сервисам, что нуждаются в изоляции внутри корпоративной сети. Соответственно, после установления VPN-соединения, необходимо активировать дополнительные сетевые маршруты, направляющие выделенный трафик не через "шлюз по умолчанию", а через L2TP-сервер. Такой функционал давно реализован через систему "хуков (hook)", запускающий произвольные скрипты при изменении состояния PPP-соединений.
Пишем простой bash-скрипт, автоматически запускаемый при инициализации PPP-соединения и добавляющий произвольный набор маршрутов с предварительной проверкой наличия опорной IP-адресации (в имени скрипта "ppp-hook" не допускается символов ".", так что для наглядности заменяем их "-"):
# vi /etc/ppp/ip-up.d/90-l2tp-gate-example-net
#!/bin/bash
if [[ "${PPP_LOCAL}" == "10.172.255.2" ]] ; then
sleep 1
ip route add 10.10.10.0/24 via 10.172.255.1
ip route add 192.168.10.0/24 via 10.172.255.1
fi
exit $?
if [[ "${PPP_LOCAL}" == "10.172.255.2" ]] ; then
sleep 1
ip route add 10.10.10.0/24 via 10.172.255.1
ip route add 192.168.10.0/24 via 10.172.255.1
fi
exit $?
Не забываем сделать скрипт "исполняемым" (в противном случае подсистема PPP проигнорирует его):
# chmod +x /etc/ppp/ip-up.d/90-l2tp-gate-example-net
Ручное управление VPN-соединениями.
Учитывая то, что сервисы шифрования и поддержки PPP-туннеля реализованы в двух независимых подсистемах, ради обеспечения должного уровня безопасности важно не забывать об очерёдности их включения и выключения - чтобы в какой-то момент через недоверенные участки сети не пошёл незашифрованный трафик.
Последовательная инициализации IPsec-шифрования и L2TP-туннеля:
# ipsec up ipsec-gate.example.net && xl2tpd-control connect l2tp-gate.example.net
Последовательное выключение L2TP-туннеля и IPsec-шифрования:
# xl2tpd-control disconnect l2tp-gate.example.net && xl2tpd-control remove l2tp-gate.example.net && ipsec down ipsec-gate.example.net
Автоматизация запуска посредством "ifupdown".
При автоматическом запуске IPsec-шифрования и L2TP-туннеля нет необходимости отдельно стартовать эти сервисы. Но, в каких-то конфигурациях это может потребоваться:
# vi /etc/network/interfaces
....
iface eth0 inet dhcp
....
sleep 5 && post-up ipsec up ipsec-gate.example.net && xl2tpd-control connect l2tp-gate.example.net &
iface eth0 inet dhcp
....
sleep 5 && post-up ipsec up ipsec-gate.example.net && xl2tpd-control connect l2tp-gate.example.net &
Автоматизация запуска посредством "Netplan".
Новомодная подсистема управления сетевой конфигурацией "Netplan" не имеет встроенного обработчика изменения состояния, что странно. Придётся воспользоваться дополнительным инструментом "Network Dispatcher":
# apt-get install networkd-dispatcher
# vi /etc/networkd-dispatcher/routable.d/90-ifup-eth0-hooks
#!/bin/bash
if [[ "${IFACE}" == "eth0" ]] ; then
sleep 5
ipsec up ipsec-gate.example.net && xl2tpd-control connect l2tp-gate.example.net &
fi
exit $?
if [[ "${IFACE}" == "eth0" ]] ; then
sleep 5
ipsec up ipsec-gate.example.net && xl2tpd-control connect l2tp-gate.example.net &
fi
exit $?
Добавляем скрипту статус "исполняемого" (иначе "networkd-dispatcher" проигнорирует его):
# chmod +x /etc/networkd-dispatcher/routable.d/90-ifup-eth0-hooks