Возврат к перенаправленному общедоступному IP-адресу из локальной сети - шпилька NAT

Это канонический вопрос о шпильке NAT (Loopback NAT).

Общая форма этого вопроса:

У нас есть сеть с клиентами, сервером и маршрутизатором NAT. На маршрутизаторе имеется переадресация портов на сервер, поэтому некоторые его службы доступны извне. У нас есть DNS, указывающий на внешний IP. Клиентам локальной сети не удается подключиться, но внешняя работа.

  • Почему это не удается?
  • Как я могу создать единую схему именования (DNS-имена, которые работают как локально, так и внешне)?

Этот вопрос был объединен с несколькими другими вопросами. Первоначально они ссылались на FreeBSD, D-Link, Microtik и другое оборудование. Однако все они пытаются решить одну и ту же проблему.

11 ответов

Решение

То, что вы ищете, называется "шпилька NAT". Запросы от внутреннего интерфейса для IP-адреса, назначенного внешнему интерфейсу, должны быть NAT-такими, как если бы они поступали от интерфейса внешней стороны.

Я совсем не знаком с FreeBSD, но, читая руководство "pf" для OpenBSD ( http://www.openbsd.org/faq/pf/rdr.html) предлагаемые решения DNS с расщепленным горизонтом, используя Сеть DMZ или TCP-прокси заставляют меня поверить, что "pf" не поддерживает шпильку NAT.

Я хотел бы взглянуть на маршрут DNS с разделенным горизонтом и не использовать IP-адреса в URL-адресах внутри, а вместо этого использовать имена.

Поскольку этот вопрос был повышен до канонического вопроса о шпильке NAT, я подумал, что, вероятно, у него должен быть ответ, который был бы более общепринятым, чем принятый в настоящее время, который (хотя и превосходный) относится конкретно к FreeBSD.

Этот вопрос относится к услугам, предоставляемым серверами в сетях IPv4 с адресацией RFC1918, которые доступны внешним пользователям путем введения целевого NAT (DNAT) на шлюзе. Внутренние пользователи затем пытаются получить доступ к этим службам через внешний адрес. Их пакет отправляется от клиента к устройству шлюза, который перезаписывает адрес назначения и немедленно внедряет его обратно во внутреннюю сеть. Именно этот резкий разворот, который делает пакет на шлюзе, приводит к названию шпильки NAT, по аналогии с поворотом шпильки.

Проблема возникает, когда устройство шлюза перезаписывает адрес назначения, но не адрес источника. Затем сервер получает пакет с внутренним адресом назначения (своим собственным) и внутренним адресом источника (клиентского); он знает, что может ответить прямо на такой адрес, поэтому он и делает. Поскольку этот ответ является прямым, он не проходит через шлюз, который, следовательно, никогда не получает возможности сбалансировать влияние NAT входящего назначения на исходный пакет путем перезаписи исходного адреса возвращаемого пакета.

Таким образом, клиент отправляет пакет на внешний IP-адрес, но получает ответ с внутреннего IP-адреса. Он не знает, что два пакета являются частью одного и того же диалога, поэтому никакой беседы не происходит.

Решение заключается в том, что для пакетов, которым требуется такой NAT назначения и которые достигают шлюза из внутренней сети, также необходимо выполнить NAT источника (SNAT) для входящего пакета, обычно путем перезаписи адреса источника в адрес шлюза. Затем сервер считает, что клиент является самим шлюзом, и отвечает непосредственно на него. Это, в свою очередь, дает шлюзу возможность сбалансировать влияние как DNAT, так и SNAT на входящий пакет путем перезаписи адресов источника и назначения в возвращаемом пакете.

Клиент думает, что разговаривает с внешним сервером. Сервер думает, что говорит с устройством шлюза. Все стороны счастливы. Диаграмма может быть полезна на этом этапе:

введите описание здесь

Некоторые потребительские шлюзы достаточно ярки, чтобы распознавать те пакеты, для которых необходим второй шаг NAT, и они, вероятно, будут работать "из коробки" в сценарии с заколками NAT. Другие нет, и поэтому не будут, и вряд ли их можно заставить работать. Обсуждение устройств потребительского уровня, не относящихся к теме отказа сервера.

Правильным сетевым устройствам, как правило, говорят, что они работают, но - потому что они не занимаются вторым угадыванием своих администраторов - им нужно сказать, что они делают это. Linux использует iptables сделать DNAT таким образом:

iptables -t nat -A PREROUTING  -p tcp --dport 80 -j DNAT --to-destination 192.168.3.11

который включит простой DNAT для порта HTTP, для внутреннего сервера на 192.168.3.11, Но для того, чтобы включить NAT для шпильки, необходимо также правило, такое как:

iptables -t nat -A POSTROUTING -d 192.168.3.11 -p tcp --dport 80 -j MASQUERADE

Обратите внимание, что для правильной работы такие правила должны находиться в нужном месте в соответствующих цепочках и в зависимости от настроек в filter В цепочке могут потребоваться дополнительные правила, чтобы разрешить прохождение NATted трафика. Все подобные обсуждения выходят за рамки этого ответа.

Но, как уже говорили другие, правильное включение шпильки NAT не лучший способ справиться с этой проблемой. Лучше всего использовать DNS с разделенным горизонтом, где ваша организация предоставляет разные ответы для исходного поиска в зависимости от того, где находится запрашивающий клиент, либо с помощью разных физических серверов для внутренних и внешних пользователей, либо путем настройки DNS-сервера для ответа по-разному в соответствии с адрес запрашивающего клиента.

Проблема здесь в том, что ваш маршрутизатор не NAT адрес вашего внутреннего клиента. Таким образом, TCP рукопожатие не удается.

Давайте предположим, что следующие IP

  • Клиент: 192.168.1.3
  • Сервер: 192.168.1.2
  • Маршрутизатор внутренний: 192.168.1
  • Маршрутизатор внешний: 123.123.123.1

Вот что происходит:

  1. Клиент (192.168.1.3) отправляет TCP-SYN на ваш внешний IP, порт 80 (123.123.123.1:80)
  2. Маршрутизатор видит правило переадресации портов и пересылает пакет на сервер (192.168.1.2:80) без изменения исходного IP-адреса (192.168.1.3)
  3. Клиент ожидает SYN-ACK с внешнего IP
  4. Сервер отправляет свой ответ обратно клиенту напрямую, потому что он находится в той же подсети. Он не отправляет пакет на маршрутизатор, который может изменить NAT.
  5. Клиент получает SYN-ACK с 192.168.1.2 вместо 123.123.123.1. И отбрасывает это.
  6. Клиент все еще ожидает SYN-ACK с 123.123.123.1 и время ожидания истекло.

Почему бы не использовать DNS с расщепленным горизонтом вместо жесткого IP-адреса везде? У вас будет ext.yourdomain, указывающий на 217.xxx снаружи, а затем на 192.xxx внутри.

Недавно ответил на аналогичный вопрос: статический NAT Cisco не работает на стороне локальной сети и просто понял, что это канонический вопрос. Итак, позвольте мне резюмировать решение здесь.

Прежде всего: забудьте о NAT (если можете) - вопрос вовсе не в настройке NAT. Речь идет о доступе к серверу, размещенному за NAT, из Интернета и локальной сети. Использование двух зон DNS является жизнеспособной альтернативой, но не всегда решением проблемы. Но решение существует и невероятно просто (хотя, возможно, не идеально):

(1) на сервере: добавьте общедоступный IP-адрес в качестве вторичного IP-адреса в сетевой интерфейс сервера с маской 255.255.255.255 (веб-служба или все, что вы хотите на сервере также должны прослушивать этот IP-адрес); все современные операционные системы позволят вам сделать это (или вместо добавления вторичного IP к первичному интерфейсу можно использовать петлевой интерфейс с назначенным ему публичным IP-адресом).

(2) на хостах LAN: добавьте маршрут хоста для общедоступного IP-адреса, например, для хостов Windows используйте следующую команду: route -p add маска 203.0.113.130 255.255.255.255 192.168.1.11 (вы также можете использовать DHCP " "статический маршрут", возможность раздавать маршрут). Или, если есть (а) коммутатор (ы) L3 / маршрутизатор (ы) между клиентами и маршрутизатором, выходящим в Интернет, настройте этот маршрут хоста на этом (этих) промежуточных коммутаторе (ах) / маршрутизаторе (ах), а не на клиентов.

Для тех, кто имеет дело с трехсторонним рукопожатием TCP: он будет работать нормально в предложенной конфигурации.

Пожалуйста, оставьте отзыв (хотя бы, проголосуйте).

Если это оригинальный маршрутизатор D-Link (т. Е. Не Rev. D / Firmware Version 1.00VG от Virgin Media), вы должны иметь возможность отрегулировать настройки, чтобы обойти это. (Однако я согласен с предложением предыдущего автора о DD-WRT по многим другим причинам!)

  1. Войдите в веб-интерфейс маршрутизатора
  2. Перейдите на вкладку "Дополнительно" вверху
  3. Перейдите на вкладку "Параметры брандмауэра" слева.
  4. Нажмите кнопку-переключатель Независимая от конечной точки в разделе Фильтрация конечной точки TCP, как показано на снимке экрана ниже (или см. Эмулятор маршрутизатора на веб-сайте D-Link)
  5. Сохранить изменения; ты сделал

Скриншот веб-интерфейса роутера D-Link

Этот скриншот взят из модели Rev. C; у тебя может быть немного по другому.

С технической точки зрения лучшее решение этой проблемы - включить IPv6 в вашей сети. Когда IPv6 включен, вам нужно создать запись AAAA для вашего домена. Сохраните существующую запись A, указывающую на внешний IPv4 маршрутизатора. Создайте запись AAAA, указывающую на IPv6-адрес сервера.

У IPv6 достаточно адресов, чтобы избежать NAT, поэтому вам не понадобится шпилька NAT для IPv6. И как только вы включите IPv6 и создадите записи AAAA, любой клиент, поддерживающий RFC 8305, будет пытаться использовать IPv6 до IPv4. Это означает, что вам не нужен шпильки NAT для IPv4, потому что клиенты не будут использовать его.

Вам по-прежнему понадобится существующий IPv4 NAT для исходящих соединений и переадресация портов для входящих соединений, пока в большинстве стран мира IPv6 также не будет включен.

Это также быстрее.

Использование IPv6 даст вам лучшую производительность, чем шпилька NAT.

С помощью шпильки NAT ваш клиент отправит пакет через коммутатор на маршрутизатор, затем маршрутизатор выполнит два раунда трансляции и, наконец, отправит пакет через коммутатор на сервер. Пакеты с сервера на клиент будут проходить весь этот путь в обратном порядке.

С IPv6 вы избегаете NAT, вместо этого пакеты отправляются непосредственно через коммутатор между клиентом и сервером. Это означает, что при передаче туда и обратно вы уменьшаете количество проходов через коммутатор с 4 до 2, и вы избегаете 2 поездок через маршрутизатор и 4 трансляций, которые выполнял бы маршрутизатор. Это приводит к лучшей производительности.

Это верно, даже если вы используете коммутатор, встроенный в тот же блок, что и маршрутизатор.

Что делать, если у провайдера нет IPv6?

Если вы используете провайдера, который не поддерживает IPv6, я задам вопрос, должны ли вы размещать серверы в этой сети. Вот мои предложения о том, что делать, если провайдер в настоящее время не поддерживает IPv6.

Сначала скажите провайдеру, что вам нужен IPv6. И, возможно, напомните им, что протокол IPv6 существует уже 20 лет, поэтому им давно пора поддержать его. Если этого недостаточно, чтобы провайдер отнесся к вам серьезно, начните искать других провайдеров.

Если вы найдете провайдера с поддержкой IPv6, вы можете работать с обоими провайдерами в течение переходного периода. На маршрутизаторе, подключенном к новому провайдеру, вы можете отключить IPv4 на стороне LAN, а затем подключить стороны LAN обоих маршрутизаторов к одному коммутатору. IPv4 и IPv6 являются двумя независимыми протоколами, и, таким образом, нет проблем, если эти соединения проходят через разные маршрутизаторы. Дополнительным преимуществом является некоторая избыточность в случае сбоя одного из соединений.

Если вы не можете найти интернет-провайдера с поддержкой IPv6, вам следует подумать о переносе вашего сервера на хостинг. С сервером в хостинге вы меньше зависите от географического местоположения, и по этой причине существует большая конкуренция между провайдерами, которая поможет обеспечить тот, который удовлетворит ваши потребности.

Перемещение сервера на хостинг не даст вашим клиентам IPv6, но перемещение сервера означает, что вам больше не понадобится шпилька NAT для доступа к нему.

Чего не следует делать

Не включайте IPv6 и не создавайте записи AAAA, если у вас нет способа маршрутизировать трафик IPv6. Если ваш провайдер не поддерживает IPv6, но вы все равно хотите включить IPv6 в вашей локальной сети (возможно, с использованием адресов RFC 4193) и создать записи AAAA, он будет работать для клиентов в вашей локальной сети, которые достигают сервера в вашей локальной сети. Но связь между вашей локальной сетью и внешним миром вначале будет использовать IPv6 (который не будет работать), и вы будете полагаться на возврат к IPv4, который в лучшем случае немного медленнее или в худшем случае не происходит.

Я отвечу на мои вопросы только для того, чтобы расширить кругозор для людей с похожими проблемами.

Я связался со своим провайдером и попросил их попытаться решить мои проблемы. Они предложили мне еще один публичный IP-адрес только для сервера. Теперь у меня есть локальный трафик на стороне WAN FreeBSD, и мы создали специальные каналы для более быстрой пропускной способности для локального трафика на общедоступный IP-адрес сервера.

Поскольку я также задавал этот вопрос (см. Как получить доступ к сетевой службе NAT, находящейся за брандмауэром изнутри, используя его внешний IP-адрес?), Он был перенаправлен сюда, но ответы здесь не дали решения (в отличие от общих объяснений), позволили моему предоставить мой Linux (iptables конкретное) решение здесь, чтобы сэкономить всем несколько часов экспериментов. Этот файл находится в iptables-restore формат и может быть прочитан непосредственно в iptables (после редактирования IP-адресов, конечно). Это для веб-сервера (порт 80) и только для IPv4 - правила для IPv6 и для SSL (порт 443) аналогичны.


# Port forwarding for VM / Container access with „hairpin NAT“.
*nat
:PREROUTING ACCEPT [3:205]
:INPUT ACCEPT [59:670]
:OUTPUT ACCEPT [16:172]
:POSTROUTING ACCEPT [20:257]

# This was simple port forwarding - access works from outside but not from inside
#-A PREROUTING  -4 -p tcp -i eth0 --dport 80 -j DNAT --to web.local:80

# This is real hairpin NAT which allows „web.local“ to access itself via the VM hosts external IP.
# First we need to masquerade any traffic going out the external interface:
-A POSTROUTING -o eth0 -j MASQUERADE

# Then we need to reroute incoming traffic on the public IP to the local IP:
-A PREROUTING  -4 -p tcp -d web.public.com --dport  80 -j DNAT --to web.local:80

# And finally we need to tell the router that the source IP of any traffic
# coming from the LAN must be source-rewritten when going to the web server:
-A POSTROUTING -4 -p tcp -s lan.local/24 -d web.local --dport  80 -j SNAT --to-source web.public.com:80

COMMIT

замещать lan.local, web.local а также web.public.com с вашей локальной сетью (например, 10.0.x.0/24), локальным IP-адресом вашего веб-сервера (например, 10.0.1.2) и открытым IP-адресом вашего маршрутизатора (например, 4.5.6.7). -4 просто разрешить правила IPv6 и IPv4 в одном файле (такие строки игнорируются ip6tables). Кроме того, не забывайте указывать адреса IPv6 в [скобках], если они содержат объявления портов, например [fe0a:bd52::2]:80,

Это было все, что заставило меня вырвать голову, пытаясь на самом деле реализовать объяснения в этом вопросе. Я надеюсь, что ничего не пропустил.

Я добавлю ответ здесь, так как комментарии здесь не решают мою конкретную проблему. Я подозреваю, что это потому, что я столкнулся с неприятной ошибкой ядра Linux. Настройка:

internet <--> modem 1.1.1.1/30 <--> switch <---> LAN 10.1.1.0/24
                                      ^
        +----------------------+      |
        |              /--eth0 o <----/
        |              |       |           
        | 10.1.1.1/24 br0      |           v (antenna)
        |  1.1.1.2/30  |       |           |
        |              \-wlan0 o ----------/
        +----------------------+ 

Несмотря на сложную картину, единственным значимым изменением в ситуациях, описанных в других комментариях, является добавление программного моста br0. Это происходит потому, что шлюз также является беспроводной точкой доступа для локальной сети.

Наш шлюз все еще выполняет функции NAT для машин в локальной сети. Поскольку он имеет только 1 порт Ethernet, он вынужден делать шпильку NAT. Я подозреваю, что это должно просто работать с правилами iptables, приведенными в других комментариях здесь, но по крайней мере в ядре Linux 4.9 это не так. В версии 4.9 наш шлюз может получить доступ к Интернету, а машины в локальной сети, пытающиеся получить к нему доступ через NAT, не могут.

tcpdump показывает ответы на входящие пакеты, нажимающие eth0, но они не делают это из br0. Выполнение этой команды исправляет следующее:

ebtables -t brouter -A BROUTING -d 01:00:00:00:00:00/01:00:00:00:00:00 -j ACCEPT
ebtables -t brouter -A BROUTING -p IPv4 --ip-dst 10.1.1.0/24 -j ACCEPT
ebtables -t brouter -A BROUTING -p IPv4 --ip-src 10.1.1.0/24 -j ACCEPT
ebtables -t brouter -A BROUTING -p IPv4 -j DROP

Перед выполнением этой команды входящие пакеты обрабатываются в соответствии с поведением ядра по умолчанию, которое заключается в том, чтобы передать их мосту, а затем передать им модули маршрутизации ядра. Команда заставляет пакеты, которые не из локальной сети, обходить мост и переходить непосредственно к маршрутизации, что означает, что у моста нет возможности отбросить их. Широковещательные и многоадресные адреса должны быть соединены, иначе такие вещи, как DHCP и mDNS, не будут работать. если вы используете IPv6, вы также должны добавить правила для него.

У вас может возникнуть соблазн решить проблему с помощью этого:

brctl hairpin br0 eth0 on
brctl hairpin br0 wlan0 on

Я, конечно, был так искушен - это была моя первая попытка. Как только я сделал это, машины в локальной сети получили доступ к Интернету, так что он работает некоторое время. Затем произошло следующее (и я не хотел повторять эксперимент):

  1. Время пинга через локальную сеть до шлюза удваивалось с интервалами примерно в 10 секунд, увеличиваясь с 0,1 мс до 0, 2 мс, 0,4 мс, 0,8 мс, 2 мс и т. Д., Пока шлюз не был недоступен из локальной сети. Он пах как шторм пакетов, но STP был включен везде.
  2. Вскоре после того, как все точки беспроводного доступа умерли.
  3. При попытке диагностики того, что происходило с беспроводной связью, все IP-телефоны перезагрузились.
  4. Вскоре после этого проводные машины потеряли все контакты с локальной сетью.

Единственным выходом было перезагрузить каждую машину в здании. Единственным исключением были аппаратные переключатели, которые не могли быть перезагружены. Они должны были быть выключены.

В FreeBSD с использованием PF это просто как (в вашем файле pf.conf):

extif = "tun0"
intif = "em0"
{other rules}...
nat on $intif from any to 192.168.20.8 port 80 -> ($extif)

192.168.20.8 будет внутренним веб-сервером.

Другие вопросы по тегам