Как стать автором
Обновить

Настройка проксирования внешнего адреса через GRE туннель между Linux серверами

Привет, друзья!
Я имею два сервера, сервер А и сервер Б, оба на Debian Wheezy. На сервер А зароучено определенное количество внешних IP-адресов, а на сервер Б — один, и то он стоит за натом. И возникла у меня на днях весьма нетривиальная задача — понадобилось пробросить один из IP-адресов сервера А на сервер Б, да так, чтобы реальные IP посетителей при проксировании сохранялись, и была возможность управлять трафиком, который будет поступать на сервер Б. NAT и ip-ip отпал сам собой из-за второго условия, да и они далеко не самые производительные. И я решил смотреть в сторону GRE туннеля.
Под катом настройка и тестирования GRE-туннеля, а также проксирование внешнего IP-адреса через GRE-туннель на другой сервер.

Для начала нам нужно загрузить модуль gre на обоих серверах перед выполнением операций, поскольку это автоматически не происходит. Мы укажем это в конфигах, так что при последующих активациях интерфейса туннеля он будет подгружаться автоматически, а пока давайте подргузим его сами:
modprobe ip_gre

Далее в /etc/network/interfaces сервера А прописываем следующее:

auto tun0
iface tun0 inet static
address 10.10.10.1
netmask 255.255.255.0
pointopoint $ip_to_proxy
pre-up modprobe ip_gre
pre-up ip tunnel add tun0 mode gre local $server1_ip remote $server2_ip ttl 255 dev $dev
up ifconfig tun0 multicast
up arp -sD $ip_to_proxy $dev pub
post-down ip tunnel del tun0

А теперь я объясню, что значит каждая строчка.
auto tun0 — интерфейс будет подниматься автоматически при запуске сети. Интерфейс можно назвать как угодно, не обязательно tun0, но главное, чтобы таких же имен в системе не было.
iface tun0 inet static — интерфейс туннеля у нас будет иметь статический IP-адрес.
address 10.10.10.1 — это адрес сервера А внутри туннеля. Из сервера Б он будет доступен по команде ping именно по этому адресу.
netmask 255.255.255.0 — маска подсети.
pointopoint $ip_to_proxy — вместо $ip_to_proxy подставьте IP-адрес, который вы собираетесь пробросить на сервер Б. Нужно, чтобы такой адрес никто не слушал и он нигде не был прописан.
pre-up modprobe ip_gre — загружать модуль туннеля ip_gre автоматически перед поднятием интерфейса.
pre-up ip tunnel add tun0 mode gre local $server1_ip remote $server2_ip ttl 255 dev $dev — вместо $server1_ip подставьте существующий публичный IP-адрес сервера А, который имеет интерфейс $dev. $dev — соответственно основной интерфейс, который будет использоваться GRE. Обычно это eth0. $server2_ip — внешний адрес сервера Б (неважно, что он за натом и имеет в действительности серый адрес, это будет учтено в конфигурации для сервера Б).
up ifconfig tun0 multicast — интерфейс у нас многоадресный, поэтому нам нужно включить для него мультикаст.
up arp -sD $ip_to_proxy $dev pub — этим трюком мы обойдем необходимость проксирования ARP (net.ipv4.neigh.$dev.proxy_arp = 1 в sysctl.conf), так как мы оперируем лишь с одним адресом. Да это и неважно, но при включении proxy_arp на все интерфейсы вашего сервера будет литься промежуточный трафик от роутера, что очень нехорошо, ибо он достигает внушительных размеров. Чтобы избавиться от такого «побочного эффекта», мы и воспользуемся этой командой. Значения переменных $ip_to_proxy и $dev смотрите выше.
post-down ip tunnel del tun0 — при отключении сети удалить информацию о созданном туннеле (которую можно просмотреть при помощи команды ip tunnel).

В /etc/sysctl.conf сервера А также включаем IP Forwarding, прописав в файле net.ipv4.ip_forward = 1, сохранив его и применив обновленное значение командой sysctl -p.

На этом мы закончили с сервером А, осталось только поднять интерфейс туннеля, что мы сделаем после аналогичных настроек на сервере Б.

Внизу файла /etc/iproute2/rt_tables дописываем 200 tun0, сохраняем файл.
В /etc/network/interfaces сервера Б прописываем следующее:

auto tun0
iface tun0 inet static
address $ip_to_proxy
netmask 255.255.255.0
pointopoint 10.10.10.1
pre-up modprobe ip_gre
pre-up iptunnel add tun0 mode gre local $server2_ip remote $server1_ip ttl 255 dev $dev
up ifconfig tun0 multicast
post-up ip ru add from $ip_to_proxy lookup tun0 priority 32765
post-up ip ro add default via 10.10.10.1 dev tun0 src $ip_to_proxy table tun0
pre-down ip ro del default via 10.10.10.1 dev tun0 src $ip_to_proxy table tun0
pre-down ip ru del from $ip_to_proxy lookup tun0 priority 32765
post-down iptunnel del tun0

Я расскажу только про отличия данных от сервера А.
address $ip_to_proxy — вместо $ip_to_proxy подставляем IP адрес, который нам будет проксировать сервер А.
pre-up iptunnel add tun0 mode gre local $server2_ip remote $server1_ip ttl 255 dev $dev — отличий нет, замечу только, что в качестве $server2_ip можно указать и серый IP адрес. Обратите внимание, что если сервер за натом, и вы укажете адрес ната, а не реальный серый, то туннель работать не будет! Убедитесь, что IP, который вы сюда подставите, прибит к внешнему интерфейсу $dev (обычно eth0).
post-up ip ru add from $ip_to_proxy lookup tun0 priority 32765 — после поднятия интерфейса мы создадим правило маршрутизации для пакетов, приходящих с IP адреса, который нам будет отдавать сервер А. Правило будет иметь название tun0 и корректность его создания вы сможете проверить командой ip rule show.
post-up ip ro add default via 10.10.10.1 dev tun0 src $ip_to_proxy table tun0 — завернуть трафик для нашего туннеля внутрь вышесозданного правила маршрутизации. 10.10.10.1 — это IP адрес сервера А внутри туннеля.
Следующие два правила удалят сначало правило для заворота трафика, а затем и правило маршрутизации после того, как сеть будет отключена.

Вот и все! Теперь выполняем команду ifup tun0 на сервере А и сервере Б. Если все сделано правильно, то сервер А будет пинговаться из сервера Б по адресу 10.10.10.1, а команда traceroute $ip_to_proxy покажет, что трафик проходит через $server1_ip.

А теперь пару примеров.

Для того, чтобы убедиться, что IP-адреса клиентов реальны, а не IP сервера А, как это произойдет с NAT / ip-ip, запустим tcpdump на сервере Б:
tcpdump -i tun0 port 80
И увидим, что пакеты имеют реальные IP-адреса.
Теперь вы можете управлять трафиком, который будет проходить через сервер А на сервер Б. Для этого используйте нужные вам правила iptables в формате iptables -t raw -A PREROUTING -d $ip_to_proxy либо же iptables -t mangle -A PREROUTING -d $ip_to_proxy на сервер А. Они подействуют на трафик, который пройдет на сервер Б.
Пример: добавив на сервере А правило iptables -t mangle -A PREROUTING -d $ip_to_proxy -p tcp -m tcp --dport 80 -j DROP
, мы заблокируем для сервера Б HTTP-трафик.
Стоит еще отметить, что туннель может корректно заработать не сразу в связи с задержками в обработке ARP кешей.
Если что-то не вышло, пишите в комментариях и я постараюсь помочь. Удачи вам!
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.