Туннелирование и частные сети
Туннелирование: использование некоторого потока данных для инкапсуляции (в общем случае — произвольного) сетевого трафика.
- Шифрование контента
- Защита от пассивного анализа / фильтрации
- Платформа для включения в инфраструктуру (частные и программно определяемые сети)
Простейший туннель: IP over IP
IP_in_IP: например, для мобильных сетей
Просто добавим ip link соответствующего типа
- С указанием реальных адресов концов туннеля
- Настроим IP как обычно)
Пример на developers.redhat.com (там ещё 100500 вариантов туннелей)
Использование
Обычная схема:
client ← очень-внутренняя-сеть → router ← внутренняя-сеть → srv
- Настроим везде IP-адреса
srv — маршрут на client и конец туннеля (с помощью ip link)
# ip route add очень-внутренняя-сеть via router-IP # ip link add name ipip0 type ipip remote client-IP local srv-IP # ip link set ipip0 up # ip addr add dev ipip0 srv-адрес-туннеля
router — только ip forwarding
client — маршрут по умолчанию и конец туннеля (с помощью ip tunnel, результат тот же)
# ip route add default via router-IP # ip tunnel add ipip0 mode ipip remote srv-IP local client-IP # ip link set ipip0 up # ip addr add dev ipip0 client-адрес-туннеля # ping srv-адрес-туннеля
Посмотреть tcpdump на router
- См. на MTU: мы же запихнули IP в IP ☺
«Выход в интернет»:
- Единственная фишка — конфликт маршрута до конца туннеля и маршрута по умолчанию
Настроим NAT на srv:
# systemctl enable --now nftables.service # nft add chain inet filter masq "{type nat hook postrouting priority srcnat;}" # nft add rule inet filter masq oif eth0 masquerade
На srv:
- Удалим default route, добавим default route через туннель:
# ip route del default via router-IP # ip route add default via srv-адрес-туннеля
- Всё отвалится, потому что:
# ip route get srv-IP
⇒ Новый default route не не распространяется на конец туннеля:
# ip route add srv-IP via router-IP # ping -c3 5.255.255.242
- Удалим default route, добавим default route через туннель:
Проверим, что туннель работает
зарежем TCP на router:
# systemctl enable --now nftables.service # nft add rule inet filter forward ip protocol tcp reject
Какой-нибудь date | netcat 5.255.255.242 80 должен продолжать работать (потому что через router проходит не TCP, а IP)
Вот уже почти VPN!
- На сервере — по интерфейсу для каждого клиента
- Что хуже — каждое соединение требует отдельного IP-адреса. Если клиентов много, этих IP-адресов на сервере будет много
- ⇒ для p2p
- нет шифрования и авторизации
l2tp — фреймы в IP или UDP
Всё по документации с инкапсуляцией в UDP и для простоты без bridge
На srv:
# ip l2tp add tunnel tunnel_id 3000 peer_tunnel_id 4000 encap udp local srv-IP remote client-IP udp_sport 5000 udp_dport 6000 # ip l2tp add session tunnel_id 3000 session_id 1000 peer_session_id 2000 # ip l2tp show tunnel # ip l2tp show session # ip link set l2tpeth0 up # ip addr add 192.168.11.1 peer 192.168.11.100 dev l2tpeth0
На client всё то же самое, кроме перестановки ID в парах:
# ip l2tp add tunnel tunnel_id 4000 peer_tunnel_id 3000 encap udp local client-IP remote srv-IP udp_sport 6000 udp_dport 5000 # ip l2tp add session tunnel_id 4000 session_id 2000 peer_session_id 1000 # ip l2tp show tunnel # ip l2tp show session # ip link set l2tpeth0 up # ip addr add 192.168.11.100 peer 192.168.11.1 dev l2tpeth0 # ping 192.168.11.1
На router посмотреть, как ходит UDP
Особенности:
- У виртуального интерфейса имеется MAC-адрес!
Если применять bridge («Configure as bridged interfaces» в man-е), можно обойтись одним IP адресом (в простом варианте выше на каждый клиент нужно выделять +1 отдельный IP на сервере)
(если успеем, то пример из man с bridge)
Wireguard
- Идея та же самая: UDP-пакеты с каким-то payload
- Защита асимметричным шифрованием
- нужны пары открытый/закрытый ключ от всех участников процесса — и от сервера, и от клиентов
- дополнительно можно ключ защитить паролем
- обычно генерирует и раздаёт админ
- Есть клиенты под всякие архитектуры
Лайфхак:
- Для копипасты:
# wg genkey | tee /dev/stderr | wg pubkey
Минимальная настройка:
Сервер:
70-wg.netdev:[NetDev] 70-wg.netdev:Name = wg 70-wg.netdev:Kind = wireguard 70-wg.netdev: 70-wg.netdev:[WireGuard] 70-wg.netdev:ListenPort = 51820 70-wg.netdev:PrivateKey = сервера, никому не показывать (возможно, лучше PrivateKeyFile = …) 70-wg.netdev:# PublicKey = сервера, просто лежит тут прозапас 70-wg.netdev: 70-wg.netdev:[WireGuardPeer] 70-wg.netdev:AllowedIPs = 192.168.111.5/32 70-wg.netdev:PublicKey = клиента 70-wg.netdev: 70-wg.netdev:[WireGuardPeer] 70-wg.netdev:… 70-wg.network:[Match] 70-wg.network:Name = wg 70-wg.network: 70-wg.network:[Network] 70-wg.network:Address = 192.168.111.1/24
Клиент:
90-wg.netdev:[NetDev] 90-wg.netdev:Name = wg 90-wg.netdev:Kind = wireguard 90-wg.netdev: 90-wg.netdev:[WireGuard] 90-wg.netdev:PrivateKey = этого клиента 90-wg.netdev:# PublicKey = этого клиента, прозапас 90-wg.netdev: 90-wg.netdev:[WireGuardPeer] 90-wg.netdev:AllowedIPs = 192.168.111.0/24 90-wg.netdev:PublicKey = сервера 90-wg.netdev:Endpoint = 10.1.1.1:51820 90-wg.network:[Match] 90-wg.network:Name = wg 90-wg.network: 90-wg.network:[Network] 90-wg.network:Address = 192.168.111.5/24
Частная сеть как выход в интернет
Ещё раз: проблема сохранения исходного маршрута до tunnel endpoint. Задача довольно просто формулируется, но полна нюансов:
- При внезапном перенаправлении всего роутинга в в туннель надо умудриться не направить туда роутинг до end point-а.
Вообще любые нелокальные маршруты, про которые знал внутренний маршрутизатор, но не знает endpoint. Например, локальный DNS-сервер с внутренним DNS-ом.
В нашем случае довольно просто, ибо статика:
Сервер:
- Добавим частную подсеть в NAT
Клиент:
Добавим явный маршрут до сервера частной сети через тот же маршрутизатор, что и по умолчанию
- Добавим ещё явных маршрутов до ключевых локальных сервисов (DNS, файлопомойка, корпоративный мессенджер, whatever)
Только после этого выставим маршрутизатор по умолчанию (в нашем случае статика, поэтому без разницы) на сервер частной сети
При этом либо удаляем старый маршрут по умолчанию, либо делаем так, чтобы метрика новой записи была выше (параметр metric число команды ip route add или Metric= в секции [Route] файла .network).
Сложности:
- Хорошо, если все важные маршруты можно описать просто большим диапазоном (у нас 10/8) — а ну как нельзя?
Или, что хуже, у админов нет фантазии, и обе сети — и локальная, и частная — это 192.168.0.0/24?
Манипуляции с метрикой надо делать над исходным маршрутом, никак не связанным с частной сетью
- Манипуляции с удалением и добавлением маршрутов — это вообще контекстно-зависимый ад
wg-quick как-то решает эту проблему.
В systemd-networkd v250+ вроде бы тоже её решили, но в прошивке этого года systemd 249.16
⇒ Нужен разумный best practice. Для начала хотя бы сделать в ядре метрику по умолчанию не 0…
Рашн ВПН
Итак, покупаете вы роутер-мыльницу. Например, DLink. Настраиваете в нём доступ в интернет, например, по l2tp в каком-нибудь большом операторе. И выясняется, что
- До настройки l2tp default route смотрел куда-то внутрь приватных сетей оператора, на некоторый внутренний маршрутизатор. После настройки он смотрит на противоположный конец l2tp-туннеля.
Проблема в том, что противоположный конец l2tp-туннеля не находится в локальной сети, так что после смены default route l2tp-пакеты перестают до него доходить.
Нужно сделать так, чтобы маршрут по умолчанию смотрел на конец туннеля, а маршрут до конца туннеля — туда же, куда и раньше.
Если вам повезёт, и в мыльнице есть графа «static routes», а адрес внутреннего маршрутизатора и конца тоннеля не меняются, этот маршрут можно туда забить
- Однако DNS всё ещё не работает, потому что он тоже где-то внутри, но не в локальной сети.
- Хорошо, если вам разрешено пользоваться 8.8.8.8 или 1.1.1.1; правда, все внутренние имена хостов провайдера больше не имена
- Хорошо также, если адрес DNS-сервера всегда одинаковый — маршрут до него можно забить туда же в «static routes»
Но вам не повезло. Адрес DNS-сервера вы получаете по DHCP только после настройки туннеля (а тот, который был раньше, работает только на внутренние ресурсы), и он время от времени разный.
Так что вы идёте на форум, и скачиваете оттуда огромную таблицу маршрутизации, в которую сообщество вписывает все актуальные маршруты на концы тоннеля и на DNS-сервера, которые бывают у этого провайдера, заливаете её в мыльницу, и она чудесным образом работает. Или не работает.
Отчаявшись, вы находите в том же форуме рецепт. Надо зайти на сайт российского DLink, скачать оттуда специальную прошивку и залить её. После прошивания в настройках l2tp появляется — я не вру! — галочка «Russian VPN». И с ней всё работает!
Russian VPN — это просто несколько shell-скриптов (на мыльнице Linux же), которые делают вот что:
Запоминают маршрутизатор по умолчанию до настройки l2tp
- Настраивают l2tp-тоннель и IP-вадреса стандартным способом
Смотрят адрес DNS-сервера (он приехал по DHCP) и добавляют в таблицу маршрутизации маршрут до него и до конца туннеля через старый маршрутизатор
Всякое
IPsec — безудержно. Но работает!
- Основная фишка: транспортный режим, в котором шифруется только payload от IP…
- …идея была в том, чтобы логика сети оставалась, а в пакеты никто не заглядывал…
- …и она оказалась провальной: даже NAT не работает
- Тогда придумали туннельный режим. А он как всё такие туннели, только сложнее в поддержке.
- Основная фишка: транспортный режим, в котором шифруется только payload от IP…
- Если не wireguard, то OpenVPN — давно и проверенно работает
- … тысячи их
Д/З
Образ не изменился
Настроить выход в интернет через VPN на двух клиентах.
К srv из двух разных сетей (eth1 и eth2) подключены два клиента. VPN-адреса (на виртуальном wireguard-интерфейсе) у них из одной сети, и только пакеты из этой сети NAT-ятся наружу (iчерез eth0, использовать nftables). Должен работать также DNS (любым способом) и доступ от одного клиента к другому.
- Отчёт:
report 11 srv
networkctl status -a -n0 --no-pager
nft list ruleset
host ya.ru
ip route
wg
report 11 client и report 11 client2
networkctl status -a -n0 --no-pager
host ya.ru
ip route
wg
ping -c3 адрес_другого_клиента
Три отчёта (названия сохранить, должно быть: report.11.srv, report.11.client и report.11.client2) переслать одним письмом в качестве приложений на uneexlectures@cs.msu.ru