Различия между версиями 2 и 3
Версия 2 от 2023-05-15 13:50:00
Размер: 17089
Редактор: FrBrGeorge
Комментарий:
Версия 3 от 2023-06-02 00:50:21
Размер: 17093
Редактор: ArsenyMaslennikov
Комментарий: по всякие архитектуры
Удаления помечены так. Добавления помечены так.
Строка 109: Строка 109:
  * нужны пары откытый/закрытый ключ от всех участников процесса — и от сервера, и от клиентов   * нужны пары открытый/закрытый ключ от всех участников процесса — и от сервера, и от клиентов
Строка 112: Строка 112:
 * Есть клиенты по всякие архитектуры  * Есть клиенты под всякие архитектуры

Туннелирование и частные сети

Туннелирование: использование некоторого потока данных для инкапсуляции (в общем случае — произвольного) сетевого трафика.

  • Шифрование контента
  • Защита от пассивного анализа / фильтрации
  • Платформа для включения в инфраструктуру (частные и программно определяемые сети)

Простейший туннель: 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

Проверим, что туннель работает

  • зарежем 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

Сайт, и конечно Arch-вики

  • Идея та же самая: 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 в каком-нибудь большом операторе. И выясняется, что

  1. До настройки l2tp default route смотрел куда-то внутрь приватных сетей оператора, на некоторый внутренний маршрутизатор. После настройки он смотрит на противоположный конец l2tp-туннеля.
    • Проблема в том, что противоположный конец l2tp-туннеля не находится в локальной сети, так что после смены default route l2tp-пакеты перестают до него доходить.

    • Нужно сделать так, чтобы маршрут по умолчанию смотрел на конец туннеля, а маршрут до конца туннеля — туда же, куда и раньше.

    • Если вам повезёт, и в мыльнице есть графа «static routes», а адрес внутреннего маршрутизатора и конца тоннеля не меняются, этот маршрут можно туда забить

  2. Однако DNS всё ещё не работает, потому что он тоже где-то внутри, но не в локальной сети.
    • Хорошо, если вам разрешено пользоваться 8.8.8.8 или 1.1.1.1; правда, все внутренние имена хостов провайдера больше не имена
    • Хорошо также, если адрес DNS-сервера всегда одинаковый — маршрут до него можно забить туда же в «static routes»
  3. Но вам не повезло. Адрес DNS-сервера вы получаете по DHCP только после настройки туннеля (а тот, который был раньше, работает только на внутренние ресурсы), и он время от времени разный.

    • Так что вы идёте на форум, и скачиваете оттуда огромную таблицу маршрутизации, в которую сообщество вписывает все актуальные маршруты на концы тоннеля и на DNS-сервера, которые бывают у этого провайдера, заливаете её в мыльницу, и она чудесным образом работает. Или не работает.

Отчаявшись, вы находите в том же форуме рецепт. Надо зайти на сайт российского DLink, скачать оттуда специальную прошивку и залить её. После прошивания в настройках l2tp появляется — я не вру! — галочка «Russian VPN». И с ней всё работает!

Russian VPN — это просто несколько shell-скриптов (на мыльнице Linux же), которые делают вот что:

  • Запоминают маршрутизатор по умолчанию до настройки l2tp

  • Настраивают l2tp-тоннель и IP-вадреса стандартным способом
  • Смотрят адрес DNS-сервера (он приехал по DHCP) и добавляют в таблицу маршрутизации маршрут до него и до конца туннеля через старый маршрутизатор

Всякое

  • IPsec — безудержно. Но работает!

    • Основная фишка: транспортный режим, в котором шифруется только payload от IP…
      • …идея была в том, чтобы логика сети оставалась, а в пакеты никто не заглядывал…
      • …и она оказалась провальной: даже NAT не работает
    • Тогда придумали туннельный режим. А он как всё такие туннели, только сложнее в поддержке.
  • OpenVPN

    • Если не wireguard, то OpenVPN — давно и проверенно работает
  • https://github.com/tonarino/innernet

  • … тысячи их

Д/З

Образ не изменился

Настроить выход в интернет через VPN на двух клиентах.

  1. К srv из двух разных сетей (eth1 и eth2) подключены два клиента. VPN-адреса (на виртуальном wireguard-интерфейсе) у них из одной сети, и только пакеты из этой сети NAT-ятся наружу (iчерез eth0, использовать nftables). Должен работать также DNS (любым способом) и доступ от одного клиента к другому.

  2. Отчёт:
    1. report 11 srv

      • networkctl status -a -n0 --no-pager

      • nft list ruleset

      • host ya.ru

      • ip route

      • wg

    2. report 11 client и report 11 client2

      • networkctl status -a -n0 --no-pager

      • host ya.ru

      • ip route

      • wg

      • ping -c3 адрес_другого_клиента

  3. Три отчёта (названия сохранить, должно быть: report.11.srv, report.11.client и report.11.client2) переслать одним письмом в качестве приложений на uneexlectures@cs.msu.ru

LecturesCMC/LinuxNetwork2023/11_TunnelingVPN (последним исправлял пользователь ArsenyMaslennikov 2023-06-02 00:50:21)