«Телепортируем» белый ip-адрес на другой сервер при помощи ipip-туннеля

Совсем недавно я писал как «перенести» ip-адрес одной машины на другую машину. Само собой, трафик в описанном ранее случае, проходит через исходный сервер (и изменить это, не имея возможности полностью управлять своими адресами — невозможно). Но, класс задач, которые решает описанное мной решение, достаточно широк, в любом случае.

    Описанное ранее решение с pptp — максимально простое и подходящее для 95% случаев (кроме тех случаев, когда провайдер блокирует pptp/gre туннели). Но у него есть большой минус — pptp протокол в целом медлительный, сильно зависящий от процессора. Главный плюс же плюс pptp в данном контексте — отсутствие необходимости уже иметь выделенный адрес там, куда нужно «принести» другой адрес.

    Одним из «альтернативных» способов в данном случае выступает ipip-туннель. В отличии от pptp, здесь нет шифрования на уровне виртуальной сети — отсюда меньшая нагрузка на процессор. Скорость ipip-туннеля, само собой, из-за этого будет выше. То есть это уже эдакий enterprise-ready костыль.
    Вообще ipip-туннель, по сути своей, частный случай vpn без шифрования. Между двумя серверами делается туннель со своими ip-адресами внутри. Когда ядро видит, что пакет уходит на ip-адрес сервера на втором конце туннеля, оно «упаковывает» весь пакет внутрь другого пакета с публичными адресами серверов в полях Source и Destionation (и там ещё копируется поле Type Of Service), который отправляет уже на внешний ip-адрес сервера, с которым установлен туннель. Сервер-получатель, в свою очередь, распаковывает пакет на своей стороне и «видит» обычный пакет, предназначенный для его интерфейса (ну и что, что отличающегося от того, на который пакет прилетел), отправляет его в соответствующий интерфейс внутри себя, его получает приложение, а дальше всё как с обычным пакетом

    Говорят, что главным минусом ipip-туннелей является то, что они устанавливаются только между двумя серверами/роутерами. Вот, правда, все забывают, что любой другой протокол приватных сетей, устроен по тому же принципу (точка-точка между сервером и клиентом), за исключением очень специфичных решений, и прямой связи между клиентами мимо vpn-сервера там нет. Тем не менее, из говна и палок, свою сеть с хитрой маршрутизацией внутри на ipip-туннелях собрать всё же можно.

    На самом деле, главным минусом ipip является то, что между двумя точками туннеля обязательно должна быть прямая связь. То есть, либо они оба находятся в одной серой сети (впрочем, зачем тогда вообще туннель нужен — только для экспериментов разве что), либо у обоих серверов есть публичные адреса (и firewall между ними разрешает пропускать примерно любой трафик). Если один из серверов находится за NATом — то туннель навесить не получится. Точнее, опять же, давно придумано, как это можно сделать (при условии, что есть доступ на тазик, который NAT-ит), но там получится несимметричный туннель, который будет скорее не работать, чем работать. Пробросить конкретные порты на роутере не получится — ipip туннель не спускается до уровня приложений (как это делает, например, openvpn, который гонит всё через 1 tcp/udp порт).

Для чего я его присобачить смог:
    1) защита от ddos’a на промежуточном freebsd-сервере. Один из ip-адресов freebsd-сервера я туннелем повесил на linux-сервер и бодренько отбивал ddos с ipfw на этом промежуточном сервере. Получалось неплохо, куда лучше, чем с iptables без промежуточного сервера. По большей части потому, что freebsd-машина страдала только от избытка трафика (что при наличии ipmi не было проблемой вовсе) — поэтому я спокойно мог сидеть на ней и отбиваться от атаки. Linux-машина в это время уходила в глубокую кому (ещё б, ей приходилось апачи запускать), не откликаясь на консоль — приходилось снимать с неё трафик временно.
    2) оптимизация маршрутов трафика — я принимал трафик в Москве, в М9, и уже по туннелю гнал его на hetzner-сервер. У части пользователей канал в Германию сильно хуже, чем канал до Москвы (особенно вспоминается тот момент, когда у Билайна вообще сломалась связь с Германией почти на месяц — потери трафика ночью были под 70%, а выжать оттуда больше мегабита было вообще нельзя). Конечно, это решение во многом спорное (есть вероятность, что для большей части пользователей всё станет хуже из-за лишнего крюка), но если внимательно смотреть на географию пользователей и на их каналы — то можно сильно выиграть.
    3) российский ip-адрес на дешевеньком европейском сервере. SEO-шники всё ещё верят в российские ip-адреса и их полезность при ранжировании. А сайты могут хотеть очень много железных ресурсов. Нет, конечно можно использовать в данном случае и nginx с proxy_pass на второй сервер, но решение с ipip будет работать пошустрее.

  Эти 3 пункта с pptp не сильно возможны — pptp будет узким местом в таких схемах. Забегая вперед, расскажу, как будет вести себя ipip-туннель в данной ситуации. Максимальная скорость, которую мне удалось добиться при скачивании большого файла с машины, на которую я перевесил адрес ниже описанным способом — 80 мегабит в секунду. Между машиной-донором и машиной-приёмником удалось добиться максимальной скорости в ~84 мегабита в секунду. Скорость между моим десктопом и машиной донором — примерно 90 мегабит в секунду. То есть скорость сквозь туннель составляет более 90% от максимально достижимой. Здесь всё очень и очень неплохо (в pptp скорость падала в 3-4 раза). Проблема есть с «разгоном» tcp-сессии: из-за того, что донору приходится разгонять 2 сессии сразу (а он крайне хреново с этим справлялся) — сессия через туннель разгонялась в 2-3 раза медленнее (на глаз). Поэтому, максимальная скорость скачивания достигалась не за 2-3 секунд от начала скачивания, а через 5-10. К тому же скорость болталась вверх-вниз намного чаще (из-за того, что стало больше бутылочных горлышек на пути трафика). В целом, если у вас нет требований к минимальному latency и постоянной стабильной скорости, то получается очень и очень неплохо, само собой при условии, что между донором и приёмником хороший канал (у меня это были hetzner и digitalocean). В общем вердикт — быть тому.

  Настроить всю эту конструкцию получилось без каких-либо проблем со второй попытки (в первую попытку я sysctl-ки не дописал). Автоматизировать поднятие туннеля удалось через /etc/network/interfaces, туннель корректно отзывается на ifup/ifdown, корректно переподцепляется, если одна из машин ребутнулась. Чего я не смог проверить — что будет с туннелем через месяц, например. Максимально он у меня провисел 3 дня, за это время с коннектом всё было хорошо и туннель не упал. С другой стороны, чего ему будет? Вся эта конструкция работает в ядре и до user-space не спускается (отсюда и скорость соответствующая).

Итак, у нас имеется:

  • Сервер-донор, у которого есть ip адреса 198.51.100.2 и 198.51.100.3
  • Сервер-приемник, у которого есть ip-адрес 203.0.113.2
  • Сетевые карты с внешним адресом на обоих серверах eth0
  • Между серверами в iptables разрешен любой трафик
  • На машинах стоят более или менее свежие Debian или Ubuntu (либо любой другой debian-подобный дистрибутив)
  • Необходимость сделать так, чтобы ip-адрес 198.51.100.3 висел на сервере-приемнике и можно было бы корректно общаться с этим адресом из внешней сети через ip-ip туннель через сервер-донор

  Первым делом, понятное дело, снимаем ip-адрес 198.51.100.3 с интерфейсов сервера-донора (скорее всего, нужно будет удалить любые упоминания о нём в /etc/network/interfaces и порестартить сеть). На всякий случай проверяем, что этот адрес не пингуется ни снаружи, ни с самого сервера-донора.

Дальше на сервере-доноре в файл /etc/network/interfaces дописываем следующую конфигурацию:

# поднимать tun0 интерфейс автоматически
auto tun0
# Интерфейс tun0 у нас будет со статическим адресом внутри туннеля
iface tun0 inet static
    # адрес сервера-донора внутри туннеля - 192.168.0.1
    address 192.168.0.1
    # маска подсети - 255.255.255.255 (она же - /32, один адрес)
    netmask 255.255.255.255
    # адрес сервера-приемника внутри туннеля (в данном случае здесь должен быть тот адрес, который мы перевешиваем на сервер-приемник)
    pointopoint 198.51.100.3
    # внутри туннеля необходимо снижать mtu, чтобы пакеты лишний раз не дробились
    # так как на пакеты будут навешиваться дополнительные заголовки, то при mtu в туннеле по умолчанию каждый пакет размером 1500 байт внутри туннеля будет дробиться на 2 пакета перед отправкой.
    mtu 1350
    # Перед тем, как поднимать интерфейс tun0, нам необходимо создать ipip-туннель.
    # в данном случае мы создаём туннель между локальным адресом 198.51.100.2 (оставшийся адрес донора) и публичным адресом приёмника - 203.0.113.2. Туннель создаётся через сетевую карту eth0.
    pre-up /sbin/ip tunnel add tun0 mode ipip remote 203.0.113.2 local 198.51.100.2 dev eth0
    # после того, как мы останавливаем сетевой интерфейс tun0, необходимо удалить и ipip-туннель из системы.
    post-down /sbin/ip tunnel del tun0

На сервере-приемнике в /etc/network/interfaces дописываем следующее:

# поднимать tun0 интерфейс автоматически, внутри туннеля у tun0 статический адрес.
auto tun0
iface tun0 inet static
    # адрес сервера приемника внутри туннеля - 198.51.100.3 (это именно тот адрес, который мы "телепортируем"
    address 198.51.100.3
    # указываем маску /32
    netmask 255.255.255.255
    # указываем ip-адрес сервера-донора внутри туннеля (address из конфига донора)
    pointopoint 192.168.0.1
    # снижаем mtu внутри туннеля
    mtu 1350
    # перед включением сетевого интерфейса создаём туннель между внешними адресами сервера-донора и приемника
    pre-up /sbin/ip tunnel add tun0 mode ipip remote 198.51.100.2 local 203.0.113.2 dev eth0
    # после поднятия интерфейса создаём отдельную таблицу маршрутизации для пакетов, отправляемых от "телепортированного" адреса
    post-up /sbin/ip ru add from 198.51.100.3 lookup 17 priority 17
    # в созданной таблицы маршрутизации прописываем шлюз по умолчанию и устройство, через которые будет отправляться трафика для нашего адреса.
    post-up /sbin/ip ro add default via 192.168.0.1 dev tun0 src 198.51.100.3 table 17 mtu 1350 advmss 1310
    # перед отключением интерфейса tun0 удаляем сначала правило маршрутизации, а потом и дополнительную таблицу маршрутизации
    pre-down /sbin/ip ro del default via 192.168.0.1 dev tun0 src 198.51.100.3 table 17 mtu 1350 advmss 1310
    pre-down /sbin/ip ru del from 198.51.100.3 lookup 17 priority 17
    # после выключения tun0 удаляем и сам туннель из системы
    post-down /sbin/ip tunnel del tun0

  На сервере-доноре в /etc/sysctl.conf добавляем:

# включаем проксирование arp-запросов между интерфейсами
net.ipv4.conf.default.proxy_arp = 1
net.ipv4.conf.all.proxy_arp = 1
# включаем форвардинг ipv4-трафика:
net.ipv4.ip_forward=1

  На доноре применяем правила sysctl:

root@server1:~# sysctl -p

  На обоих серверах включаем туннель:

root@server:~# ifup tun0

  Или просто:

root@server:~# /etc/init.d/networking restart

  Проверяем, что ip-адрес, который мы переносили, пингуется снаружи:

user@laptop:~$ ping 198.51.100.3
PING 198.51.100.3 (198.51.100.3) 56(84) bytes of data.
64 bytes from 198.51.100.3: icmp_req=1 ttl=53 time=71.8 ms
64 bytes from 198.51.100.3: icmp_req=2 ttl=53 time=71.8 ms
64 bytes from 198.51.100.3: icmp_req=3 ttl=53 time=71.8 ms
...

  Проверяем, как идет трафик до этого ip-адреса снаружи:

user@laptop:~$ traceroute -n 198.51.100.3
...
11 198.51.100.2 57.694 ms 57.485 ms 57.463 ms
12 198.51.100.3 71.971 ms 71.958 ms 71.879 ms

Как видите — трафик прошел через наш сервер-донор и попал на второй сервер.

Если что-то не получилось — пишем в комментариях ;)
Удачи.

Who's online

There are currently 1 user and 2 guests online.