Туннелирование и частные сети
Туннелирование: использование некоторого потока данных для инкапсуляции (в общем случае — произвольного) сетевого трафика.
- Шифрование контента
- Защита от пассивного анализа / фильтрации
- Платформа для включения в инфраструктуру (частные и программно определяемые сети)
TODO вспомнить про SRv6 и вообще про ip route encap. Нужен пример encap/decap для Д/З.
Простейший туннель: IP over IP
TODO изобрести новый термин для «over» вместо «поверх»: «поверх» ассоциируется с «поверхностью» (обёрткой), хотя в действительности наоборот.
Использование
IP6 over IP6 — это как-то скучно! Ради большей наглядности пустим IP4 поверх IP6.
Обычная схема:
client ← очень-внутренняя-сеть → router ← внутренняя-сеть → srv
Настроим везде IP-адреса, на router подняв RAdv
srv — маршрут на client и конец туннеля (с помощью ip tunnel)
router — только IPv6 forwarding
client — маршрут по умолчанию и конец туннеля (с помощью ip link, результат тот же)
[root@router ~]# sysctl net.ipv{6,4}.conf.all.forwarding
net.ipv6.conf.all.forwarding = 1
net.ipv4.conf.all.forwarding = 0Настроим передачу IPv4 в тоннеле:
[root@client ~]# ip -6 -br a show scope global eth1 UP 2a00:f480:8:cc5:a00:27ff:fe1a:ffa/64 [root@client ~]# ip link add name over0 type ip6tnl mode ipip6 local 2a00:f480:8:cc5:a00:27ff:fe1a:ffa remote 2a00:f480:8:cde:a00:27ff:fee3:f50e [root@client ~]# ip link set over0 up [root@client ~]# ip a add 10.50.0.1/24 dev over0 # это IPv4; здесь весь префикс on-link, и назначился маршрут на over0 [root@client ~]# ip -d link show over0 <...>
[root@srv ~]# ip -6 -br a lo UNKNOWN ::1/128 eth1 UP 2a00:f480:8:cde:a00:27ff:fee3:f50e/64 fe80::a00:27ff:fee3:f50e/64 [root@srv ~]# ip tunnel add over0 mode ipip6 local 2a00:f480:8:cde:a00:27ff:fee3:f50e remote 2a00:f480:8:cc5:a00:27ff:fe1a:ffa [root@srv ~]# ip link set over0 up [root@srv ~]# ip a add 10.50.0.2/24 dev over0 # это IPv4; здесь весь префикс on-link, и назначился маршрут на over0 [root@srv ~]# ip -d link show over0 <...>
- См. на MTU: мы же запихнули IP в IP ☺
Посмотреть tcpdump на router с клиента на srv с srv на клиента
Ядро Linux приписывает к внешнему заголовку destination option "Tunnel Encap Limit" rfc2473, равный 4.
TODO Даже tshark тянет over 120 Мб собственной библиотеки. Предложить скачать результат запуска tcpdump -w и посмотреть на хосте с помощью хостового wireshark?
Паста из wireshark:
Frame 1: Packet, 146 bytes on wire (1168 bits), 146 bytes captured (1168 bits)
Ethernet II, Src: PCSSystemtec_1a:0f:fa (08:00:27:1a:0f:fa), Dst: PCSSystemtec_d7:92:2a (08:00:27:d7:92:2a)
Destination: PCSSystemtec_d7:92:2a (08:00:27:d7:92:2a)
Source: PCSSystemtec_1a:0f:fa (08:00:27:1a:0f:fa)
Type: IPv6 (0x86dd)
Internet Protocol Version 6, Src: 2a00:f480:8:cc5:a00:27ff:fe1a:ffa, Dst: 2a00:f480:8:cde:a00:27ff:fee3:f50e
0110 .... = Version: 6
.... 0000 0000 .... .... .... .... .... = Traffic Class: 0x00 (DSCP: CS0, ECN: Not-ECT)
.... 0001 1011 0001 1000 1110 = Flow Label: 0x1b18e
Payload Length: 92
Next Header: Destination Options for IPv6 (60)
Hop Limit: 64
Source Address: 2a00:f480:8:cc5:a00:27ff:fe1a:ffa
Destination Address: 2a00:f480:8:cde:a00:27ff:fee3:f50e
[Source SLAAC MAC: PCSSystemtec_1a:0f:fa (08:00:27:1a:0f:fa)]
[Destination SLAAC MAC: PCSSystemtec_e3:f5:0e (08:00:27:e3:f5:0e)]
Destination Options for IPv6
Next Header: IPIP (4)
Length: 0
[Length: 8 bytes]
Tunnel Encapsulation Limit
Type: Tunnel Encapsulation Limit (0x04)
Length: 1
Tunnel Encapsulation Limit: 4
PadN
Type: PadN (0x01)
Length: 1
PadN: 00
Internet Protocol Version 4, Src: 10.50.0.1, Dst: 10.50.0.2
Internet Control Message ProtocolTODO добавить сюда короткое толкование нужных для темы «туннели» опций из пасты, а остальные, если надо, просто снабдить короткими ссылками (в частности, текст ниже — это оверкилл)
There are two padding options which are used when necessary to align
subsequent options and to pad out the containing header to a multiple
of 8 octets in length. These padding options must be recognized by
all IPv6 implementations:
Pad1 option (alignment requirement: none)
+-+-+-+-+-+-+-+-+
| 0 |
+-+-+-+-+-+-+-+-+
NOTE! the format of the Pad1 option is a special case -- it does
not have length and value fields.
The Pad1 option is used to insert one octet of padding into the
Options area of a header. If more than one octet of padding is
required, the PadN option, described next, should be used, rather
than multiple Pad1 options.
PadN option (alignment requirement: none)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - -
| 1 | Opt Data Len | Option Data
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- - - - - - - - -
The PadN option is used to insert two or more octets of padding
into the Options area of a header. For N octets of padding, the
Opt Data Len field contains the value N-2, and the Option Data
consists of N-2 zero-valued octets.Проверим, что туннель работает
зарежем TCP на router:
# systemctl enable --now nftables.service # nft add rule inet filter forward meta l4proto tcp reject
Какой-нибудь date | netcat 10.50.0.242 80 должен продолжать работать (потому что через router проходит не TCP, а IP)
Недостатки
- На сервере — по интерфейсу для каждого клиента (и наоборот)
- Каждое соединение требует на сервере отдельного IP-адреса
- Если клиентов много, этих IP-адресов на сервере будет много. Их утомительно настраивать
- ⇒ пригодно в основном для p2p
- нет шифрования и авторизации; если нас смаршрутизировали, то мы молодец
TODO разъяснить: ipip0 — «не совсем настоящий» интерфейс (только p2p, без MAC и т. п.)
IP6 over TCP «на скорую руку» с socat
TODO переделать на TUN:/ipv6
В socat есть работа с tun/tap устройствами. Буквально в одну строку из них делается полноценный тоннель:
[root@srv ~]# socat TCP6-LISTEN:1337,fork,reuseaddr TUN:192.168.255.1/24,up </dev/null &
и
[root@client ~]# socat TCP:сервер:1337 TUN:192.168.255.27/24,up </dev/null &
Результат:
[root@client ~]# ip -4 r 192.168.200.0/24 dev tun0 proto kernel scope link src 192.168.200.2
[root@client ~]# ping -c2 192.168.200.1 PING 192.168.200.1 (192.168.200.1) 56(84) bytes of data. 64 bytes from 192.168.200.1: icmp_seq=1 ttl=64 time=3.64 ms 64 bytes from 192.168.200.1: icmp_seq=2 ttl=64 time=3.16 ms --- 192.168.200.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 3.159/3.399/3.639/0.240 ms
* посмотреть tcpdump на router
l2tp — фреймы в IP или UDP
TODO В идеале — вообще не использовать IP-адреса, а прокинуть что-то, чему нужен непосредственно L2. Если не получится — всё-таки заменить IP4 на IP6.
Можно туннелировать не IP-пакеты, а фреймы — например, при помощи L2TP.
Всё по документации — скажем, с инкапсуляцией не в IP, а в UDP; и для простоты — без bridge
- Общая схема:
srv, туннель № 300 , порт 1337 ←→ client, туннель № 400, порт 1337
srv, сеанс № 1 ←→ client, сеанс № 2
На srv (после настройки eth0 и маршрута до client):
# ip l2tp add tunnel tunnel_id 300 peer_tunnel_id 400 encap udp local srv-IP remote client-IP udp_sport 1337 udp_dport 1337 # ip l2tp add session tunnel_id 300 session_id 1 peer_session_id 2 # ip l2tp show tunnel # ip l2tp show session # ip link set l2tpeth0 up # ip addr add 192.168.11.1/24 dev l2tpeth0
На client (после настройки маршрута до srv) всё то же самое, кроме перестановки ID и портов в парах:
# ip l2tp add tunnel tunnel_id 400 peer_tunnel_id 300 encap udp local client-IP remote srv-IP udp_sport 1337 udp_dport 1337 # ip l2tp add session tunnel_id 400 session_id 2 peer_session_id 1 # ip l2tp show tunnel # ip l2tp show session # ip link set l2tpeth0 up # ip addr add 192.168.11.2/24 l2tpeth0 # ping 192.168.11.1
На router посмотреть, как ходит UDP (а TCP с прошлого раза зарезан)
Особенности:
У виртуального интерфейса имеется MAC-адрес — это практически настоящий виртуальный Ethrenet-сегмент
Один серверный IP:
подключения определяются сеансами (session), полностью независимыми от уровня IP;
сеансы уложены в тоннель (tunnel), спецификация которого состоит аж из шести чисел (ID тоннеля, IP-адрес и порт с обеих сторон)
- Сеансами в таблице удобно манипулировать автоматически
- Всё ещё никакого шифрования / защиты от пассивного анализа
Виртуальный интерфейс l2tpeth можно добавить в bridge c локальным интерфейсом — тогда абоненты из туннелей окажутся в одном сегменте с абонентами локальной сети
WireGuard
- Идея та же самая: UDP-пакеты с каким-то payload
- Защита асимметричным шифрованием
- нужны пары открытый/закрытый ключ от всех участников процесса — и от сервера, и от клиентов
- дополнительно можно ключ защитить паролем
- обычно генерирует и раздаёт админ
- Есть клиенты под всякие архитектуры
Linux: управляется утилитой wg
TODO Переделать grep "" … в cat …
Начальная настройка:
[root@srv ~]# grep -r "" /etc/systemd/network* /etc/systemd/network/50-intnet.network:[Match] /etc/systemd/network/50-intnet.network:Name = eth1 /etc/systemd/network/50-intnet.network: /etc/systemd/network/50-intnet.network:[Network] /etc/systemd/network/50-intnet.network:Address=2a00:f480:8:e50::918/64 /etc/systemd/network/50-intnet.network: /etc/systemd/network/50-intnet.network:[Route] /etc/systemd/network/50-intnet.network:Destination=2a00:f480:8:e60::/64 /etc/systemd/network/50-intnet.network:Gateway=_ipv6ra
TODO Пояснения относительно Gateway=_ipv6ra
[root@router ~]# grep -r "" /etc/systemd/network* /etc/systemd/network/50-intnet.network:[Match] /etc/systemd/network/50-intnet.network:Name = eth1 /etc/systemd/network/50-intnet.network: /etc/systemd/network/50-intnet.network:[Network] /etc/systemd/network/50-intnet.network:Address=2a00:f480:8:e50::919/64 /etc/systemd/network/60-deepnet.network:[Match] /etc/systemd/network/60-deepnet.network:Name=eth2 /etc/systemd/network/60-deepnet.network: /etc/systemd/network/60-deepnet.network:[Network] /etc/systemd/network/60-deepnet.network:Address=2a00:f480:8:e60::919/64 /etc/systemd/networkd.conf:[Network] /etc/systemd/networkd.conf:IPv6Forwarding=yes
[root@client ~]# grep -r "" /etc/systemd/network* /etc/systemd/network/60-deepnet.network:[Match] /etc/systemd/network/60-deepnet.network:Name=eth1 /etc/systemd/network/60-deepnet.network: /etc/systemd/network/60-deepnet.network:[Network] /etc/systemd/network/60-deepnet.network:Address=2a00:f480:8:e60::91a/64 /etc/systemd/network/60-deepnet.network: /etc/systemd/network/60-deepnet.network:[Route] /etc/systemd/network/60-deepnet.network:Destination=2a00:f480:8:e50::/64 /etc/systemd/network/60-deepnet.network:Gateway=_ipv6ra
Ручная настройка
Лайфхак для копипасты:
# wg genkey | tee /dev/stderr | wg pubkey
wg genkey — генерирует секретный ключ
| wg pubkey — генерирует из него открытый ключ
| tee /dev/stderr — дублирует секретный ключ на stderr
Для настройки вручную на сервере нужно
- создать виртуальное устройство
# ip link add dev interface type wireguard
- настроить его адрес
# ip addr add …
- настроить параметры wireguard-сервера — порт и приватный ключ
# wg set interface listen-port port private-key /path/to/file.key
- настроить параметры wireguard-подключения — открытый ключ и диапазон доступных клиенту адресов
# wg set interface peer key allowed-ips network
На клиенте:
- создать виртуальное устройство
- настроить его адрес
- настроить приватный ключ
# wg set interface private-key /path/to/file.key
- настроить параметры wireguard-подключения к серверу — открытый ключ и адрес/порт сервера
# wg set interface peer key allowed-ips network endpoint address:port
TODO добавить конкретный пример (хотя бы на уровне ip a / wg showconf)
Настройка средствами networkd
TODO Перейти на PrivateKeyFile
Для systemd-networkd:
Сервер:
Создать и настроить устройство — /etc/systemd/network/70-wg.netdev
[NetDev] Name = wg Kind = wireguard [WireGuard] ListenPort = порт PrivateKey = сервера, никому не показывать (возможно, лучше PrivateKeyFile = …) # PublicKey = сервера, просто лежит тут про запас [WireGuardPeer] AllowedIPs = 192.168.111.5/32 PublicKey = клиентаPrivateKey = — плохая идея (как минимум надо сделать chgrp systemd-network и chmod o-r)
Настроить сеть — /etc/systemd/network/70-wg.network
[Match] Name = wg [Network] Address = 192.168.111.1/24
Клиент:
/etc/systemd/network/70-wg.netdev
[NetDev] Name = wg Kind = wireguard [WireGuard] PrivateKey = этого клиента # PublicKey = этого клиента, про запас [WireGuardPeer] AllowedIPs = 192.168.111.0/24 PublicKey = сервера Endpoint = адрес:порт
/etc/systemd/network/70-wg.network
[Match] Name = wg [Network] Address = 192.168.111.5/24
Д/З
wg — route encap/decap — наблюдатель — route encap/decap — wg
