Настраивать проброс портов и общий доступ к интернету через один публичный адрес умеют все, но не все знают, что возможности NAT в netfilter гораздо шире. Сегодня я покажу вам эти возможности с помощью синтаксиса команды iptables.
Самые новые версии некоторых дистрибутивов уже используют nftables, но до ухода iptables в прошлое еще далеко. Поскольку популярные операционные маршрутизаторов, даже проприетарные, сейчас основаны на Linux, многое из данной статьи можно применить и к ним, при условии что их интерфейс не ограничивает формат опций искусственно, а просто передает их в правила iptables.
1:1 NAT
Все, кто использовал Amazon EC2, online.net и другие облачные платформы, наверняка с ним уже сталкивались. Публичные адреса для виртуалок настроить в админке можно, но самой виртуалке при этом выдается частный адрес.
Чтобы это сделать, нужно всего лишь указать по одному адресу вместо целой сети в --source/--destination и --to-destination/--to-source соответственно.
Вот команды для сопоставления частного адреса 10.0.0.10 публичному 102.0.2.10:
1 2 |
$ sudo iptables -t nat -I PREROUTING -d 192.0.2.10 -j DNAT --to-destination 10.0.0.10 $ sudo iptables -t nat -I POSTROUTING -s 10.0.0.10 -j SNAT --to-source 192.0.2.10 |
Если нет нужды подключаться к внутреннему адресу из интернета, достаточно одного правила SNAT — об ответах позаботится модуль conntrack, если он не выключен.
NETMAP
Что делать, если нужно транслировать не один адрес, а целую сеть? Нередко одной компании бывает нужно подключиться к сети другой — например, к поставщику технической поддержки или иных услуг. Проблемы начинаются, если у поставщика уже есть клиент с такой же сетью, как у вас.
В такой ситуации на помощь приходит опция NETMAP, которая транслирует одну сеть в другую.
Полезно знать, что сети для частного использования из RFC 1918 на самом деле очень большого размера. Вот они:
- 10.0.0.0/8 (до 10.255.255.255.255);
- 172.16.0.0/12 (до 172.31.255.255);
- 192.168.0.0/16 (до 192.168.255.255).
Если выбрать менее популярные адреса, чем 192.168.(0|1).0 или 10.0.0.0, с конфликтом можно не столкнуться никогда.
Предположим, в компании-клиенте используется сеть 192.168.0.0/24, а в компании-поставщике — 10.85.0.0/24. Мы сделаем вид, что в нашей компании сеть не 192.168.0.0/24, а 172.17.18.0/24:
1 2 |
$ sudo iptables -t nat -I POSTROUTING -s 192.168.0.0/24 -d 10.85.0.0/24 -j NETMAP --to 172.17.18.0/24 $ sudo iptables -t nat -I PREROUTING -s 10.85.0.0/24 -d 172.17.18.0/24 -j NETNAP --to 192.168.0.0/24 |
В отличие от SNAT и DNAT, она заменяет не весь адрес источника или назначения, а только адрес хоста — ту часть, где в маске сети нули. Адрес 192.168.0.10 всегда транслируется в 172.17.18.10, так что этот метод отлично подходит для двусторонней работы.
Если соединения инициируются только с внутренней или внешней стороны, здесь тоже можно обойтись без второго правила, достаточно настроить только PREROUTING для соединений снаружи или POSTROUTING для соединений изнутри, а остальное conntrack сделает за нас.
Очевидный недостаток — для правильной работы сети должны быть одинакового размера. Никакой проверки или даже требования предоставить опцию -s/-d в iptables нет. Если сеть источника меньше сети назначения, то вместе с адресом хоста NETMAP изменит и часть адреса сети. Если сеть источника больше — изуродует адрес хоста. Сетевой стек Linux вообще без предрассудков — делает ровно то, о чем его просят, даже если в этом нет никакого смысла.
Этот вид NAT также работает с IPv6:
1 |
$ ip6tables -t nat -I POSTROUTING -s 2001:db8:aaaa::/64 -j NETMAP --to 2001:db8:bbbb::/64 |
NPT (Network Prefix Translation)
Более правильным решением для трансляции сетей IPv6 будет функция NPTv6, описанная в RFC 6296. Она не отслеживает соединения, так что ее производительность лучше, но потребуются два правила на вход и на выход:
1 2 |
$ sudo ip6tables -t mangle -I POSTROUTING -o eth0 -j SNPT --src-pfx 2001:db8:aaaa::/64 --dst-pfx 2001:db8:bbbb::/64 $ sudo ip6tables -t mangle -I PREROUTING -i eth0 -j SNPT --src-pfx 2001:db8:bbbb::/64 --dst-pfx 2001:db8:aaaa::/64 |
NAT без conntrack
Подсистема отслеживания соединений (conntrack) решает много проблем и экономит время при настройке. Правила с опцией --state ESTABLISHED,RELATED есть почти у каждого пользователя iptables, без них пришлось бы писать два полных набора правил на вход и на выход. Кроме того, conntrack helpers (модули nf_nat_ftp и прочие) сильно упрощают работу с протоколами, которые требуют более одного соединения.
Обратная сторона удобства — меньшая производительность. В случае с NPTv6, да и с NETMAP и 1:1 NAT, если все равно указаны оба правила и опции никак не ограничивают протокол и порт, отслеживание состояний не приносит особой пользы и его можно было бы отключить.
Отключить отслеживание можно следующим образом:
1 2 |
$ sudo iptables -t raw -I PREROUTING -d 192.168.0.0/24 -j NOTRACK $ sudo iptables -t raw -I OUTPUT -s 10.85.0.0/24 -j NOTRACK |
Для IPv6 все так же, только с командой ip6tables.
С NOTRACK нужно быть очень осторожным. Если у вас есть правила с опцией --state, они, очевидно, перестанут работать, что может грозить потерей удаленного доступа к устройству. Убедитесь, что в правило попадет только трафик, который действительно не требует отслеживания состояний!
Балансировка трафика с помощью NAT
Классический masquerade транслирует множество внутренних адресов в один внешний. Если вывернуть это правило наизнанку, оно будет транслировать один внешний адрес во множество внутренних.
Это можно использовать для простой балансировки трафика. В отличие от nginx или varnish, балансировка на сетевом уровне ничего не знает о протоколе уровня приложений, но зато не зависит от конкретного протокола и работает с приложениями, для которых специализированных решений не существует.
Предположим, в нашей сети есть пять серверов с адресами 10.0.0.10–10.0.0.15 и мы хотим балансировать запросы к внешнему адресу 192.0.2.10 между ними. Это можно сделать следующей командой:
1 |
$ sudo iptables -I PREROUTING -d 192.0.2.10/32 -j DNAT --to-destination 10.0.0.10-10.0.0.15 |
При включенном conntrack такая балансировка работает на уровне соединений, а не пакетов и вполне годится для протоколов, которые требуют постоянного соединения, вроде SSH или RDP. Единственный недостаток в том, что внутренние адреса серверов должны быть в непрерывном диапазоне. Никакого способа указать список адресов или сеть не предусмотрено.
С помощью дополнительных опций --random и --persistent можно повлиять на то, будет соединение с одного внешнего адреса с большей вероятностью отправлено на один и тот же внутренний адрес или нет.
Если поменять PREROUTING на POSTROUTING и -d на -s, можно транслировать один внутренний адрес во множество внешних. Я не знаю, кому и зачем это может потребоваться, но техническая возможность у вас есть.
Hairpin
Вы, возможно, уже сталкивались с ситуацией, когда с внешнего адреса маршрутизатора проброшен порт на сервер внутри сети, но при этом попасть на сервер по внешнему адресу изнутри сети не получается.
Правильное решение в этом случае — split horizon DNS. Многие серверы DNS, включая самый популярный BIND, умеют выдавать разный результат в зависимости от адреса клиента. Но в большинстве сетей нет своего сервера, и не всегда он нужен.
В этом случае приходит на помощь hairpin или NAT reflection. Предположим, у вас есть такое правило для проброса порта на веб-сервер:
1 |
$ sudo iptables -t nat -I PREROUTING -p tcp --dport 80 -d 203.0.113.10 -j DNAT --to-destination 10.0.0.20 |
Если из сети зайти на http://203.0.113.10, соединение отвалится с тайм-аутом. Если запустить tcpdump или tshark, можно увидеть, что на сервер пакеты приходят правильно. Проблема здесь в том, что, если вы отправляете запрос с 10.0.0.123, сервер посылает ответ не маршрутизатору, а напрямую обратно к 10.0.0.123, поскольку они в одной сети.
Остается одно: пропускать весь трафик из сети в нее саму через маршрутизатор:
1 |
$ sudo iptables -t nat -I POSTROUTING -s 10.0.0.0/24 -d 10.0.0.0/24 -j MASQUERADE |
NAT в ebtables
Обычно NAT ассоциируется с сетевым уровнем, но это не значит, что его нет на канальном. В кадрах Ethernet есть MAC-адреса источника и назначения, и ebtables позволяет их поменять.
К примеру, вам нужно перенаправить зеркалированный трафик с порта коммутатора в локальный процесс для анализа. Поменять IP-адрес назначения, чтобы пакеты попали в локальный процесс, можно с помощью iptables, но сначала нужно сделать так, чтобы MAC-адрес назначения совпадал с адресом сетевой карты, иначе ядро отбросит этот трафик, даже не посмотрев на IP.
Тут нам на помощь и придет ebtables:
1 |
$ sudo ebtables -t broute -I BROUTING -i eth0 -j dnat --to-destination 00:aa:bb:cc:dd:ee |
Заключение
Не забывайте, что в недрах man iptables-extensions всегда можно отыскать что-то интересное. Не ограничивайтесь чужими статьями, читайте первоисточники сам и, если придумаете что-то интересное, не стесняйтесь предложить нам статью.