[Извинения за долгую прелюдию; вопрос на полпути.]
У меня есть работающая установка OpenVPN, при которой VPN-сервер подталкивает маршрут обратно к одному клиенту (далее называемому «маршрутизатор»), который затем может предоставить свою собственную подсеть машине, на которой запущен сервер, а также другим машинам, на которых запущен VPN-клиент. Это можно сделать, просто заставив маршрутизатор использовать SNAT
цель из iptables
. Так, например, предположим, что сервер VPN и другие непримечательные клиенты находятся в сети 10.0.77.0/24, VPN создает tun0
интерфейс, охватывающий 192.168.252.0/24, а частная подсеть - 192.168.33.0/24. В конфигурации сервера OpenVPN сказано (среди прочего)
client-to-client
route 192.168.33.0 255.255.255.0
push "route 192.168.33.0 255.255.255.0"
Когда «маршрутизатор» Linux, скажем, 192.168.33.10, подключается к VPN, он получает маршрут, поэтому его таблица выглядит так:
192.168.33.0 * 255.255.255.0 U 0 0 0 eth1
192.168.252.0 192.168.252.5 255.255.255.0 UG 0 0 0 tun0
192.168.252.5 * 255.255.255.255 UH 0 0 0 tun0
а затем он настроен на запуск
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT --to-source 192.168.33.10
Он также может добавить iptables
правил для создания брандмауэра, но приведенного выше достаточно, чтобы позволить машине, на которой запущен сервер OpenVPN (или другой клиент), подключаться, скажем, к 192.168.33.11: пакеты отправляются через tun0
к маршрутизатору, который использует SNAT для установки собственного IP-адреса источника 192.168.33.10, а затем перенаправляет пакеты на свой родственный компьютер 192.168.33.11. Ответные пакеты отправляются маршрутизатору, который затем пересылает их обратно через туннель, и все в порядке. Так, например, на 192.168.33.11 я могу
nc -l localhost 9999
и из 10.0.77.13 (какой-то другой VPN-клиент) я могу
nc 192.168.33.11 9999
и установить связь. Все идет нормально.
Обратите внимание, что без изменений выполняются на физическом маршрутизаторе в любой сети; «маршрутизатор» в кавычках (случайная машина, на которой запущен клиент VPN) должен использовать SNAT, чтобы другие машины в его сети могли отправлять ответные пакеты обратно через VPN. Для целей этого вопроса я не «контролирую» ни одну из сетей: я могу добавить только некоторые машины с их собственными правилами маршрутизации и VPN-клиентами или серверами.
Теперь к проблеме: допустим, две сети (ни одна из которых не находится в общедоступном Интернете) на самом деле перекрываются в своих фактических диапазонах. Итак, в этом примере частная подсеть - это не 192.168.33.11, а также 10.0.77.0/24. И давайте предположим, что перенумерация любой сети просто не вариант. Таким образом, помимо использования SNAT, позволяющего маршрутизатору пересылать пакеты, мне нужно переназначить частную сеть маршрутизатора на другой диапазон IP-адресов с точки зрения сервера OpenVPN. Допустим, я выбираю 10.0.78.0/24 в качестве диапазона виртуальной сети:
route 10.0.78.0 255.255.255.0
push "route 10.0.78.0 255.255.255.0"
и с серверной машины OpenVPN я хочу, чтобы подключения к 10.0.78.11 проходили через туннель и обрабатывались с помощью SNAT, как и раньше, но я также хочу, чтобы адрес назначения в подсети маршрутизатора был 10.0.77.11. Как мне настроить iptables
сделать это?
В NETMAP
цель смотрел как будто это было то, что я хотел, но я не мог заставить его работать:
iptables -t nat -A PREROUTING -i tun0 -j NETMAP --to 10.0.77.0/24
iptables -t nat -A POSTROUTING -s 192.168.252.0/24 -j SNAT --to-source 10.0.77.10
С сервера OpenVPN я могу пинговать 10.0.78.10 (т.е. переназначенный адрес маршрутизатора), когда NETMAP
target добавлен, значит, что-то делает.
tcpdump -i tun0
запустить на маршрутизаторе во время этого пинга ICMP echo request
и ICMP echo reply
как и ожидалось. Но если я попытаюсь выполнить эхо-запрос 10.0.78.11 (т. Е. Переназначенный адрес родственной машины в подсети маршрутизатора), я не получу ответов, и tcpdump
на роутере показывает запросы, но не ответы; tcpdump
на 10.0.77.11 (машина-брат) вообще нет пакетов. Также на роутере работают:
iptables -t nat -L -v
показывает пакеты, обрабатываемые NETMAP
но никто SNAT
. Так кажется, что NETMAP
цель каким-то образом вытесняет SNAT
цель?
По сути, я хочу, чтобы при получении маршрутизатором пакета tun0
например, из 192.168.252.5 (адрес сервера OpenVPN в туннеле), предназначенный для 10.0.78.11, он переписывается в два способами: адрес назначения изменяется на 10.0.77.11, а адрес источника изменяется на 10.0.77.10 (при этом выбирается новый порт источника, чтобы SNAT мог отслеживать, какое это соединение). Затем, когда 10.0.77.11 отправляет ответ на поддельный порт на 10.0.77.10, маршрутизатор должен отменить процесс, отправив пакет обратно на 192.168.252.5 на tun0
с поддельным адресом источника 10.0.78.11. Жестяная банка NETMAP
сделать это, или может любая другая цель в iptables
сделай это?
Другие вещи, которые я пытался безуспешно: настроить маршрутизатор для прокси-ARP; добавление диапазона виртуальной сети в таблицу маршрутизации. Но такое ощущение, что в таких вещах не должно быть необходимости.
Обновить: В этом контексте меня совершенно не волнует DNS - требуется связаться только с горсткой компьютеров в «частной подсети», поэтому использование IP-адресов допустимо.
Это большой беспорядок, и вам, честно говоря, лучше будет перенумерация, чем создание сети уродливых NAT с конфликтующими адресами. Другие решения, которые вам понадобятся для поддержки этого (например, DNS с разделением горизонта с несколькими зонами, возможно, с автоматическим обновлением), будут сложными и беспорядочными, и каждый последующий человек, который будет иметь дело с этой сетью, будет вечно проклинать ваше имя и сжигать изображения ты и твоя команда.
Тем не менее, похоже, что проблема, с которой вы столкнулись, заключается в том, что хосты на одной стороне NAT (видите? Я даже не могу правильно описать стороны NAT, потому что это беспорядок) не имеют обратного пути к хостам с другой стороны. Вы также должны добавить правило NETMAP для обратного пути (полагаю, пакеты, поступающие от eth1).
Но ждать! Это делается в цепочке предварительной маршрутизации. Таким образом, адрес назначения будет установлен до того, как будет принято решение о маршрутизации, что в значительной степени должно работать ... но у вашего маршрутизатора есть два маршрута для "конфликтующей" сети. Таким образом, он будет определять приоритеты обычными способами (сначала самый узкий совпадающий префикс; метрика для разрыва связей), в результате чего некоторые пакеты будут отражаться обратно в сеть, из которой они пришли, вместо того, чтобы маршрутизироваться через NAT. Они тоже будут с исходным кодом. К сожалению, исходный адрес не используется в маршрутизации, поэтому вы не можете использовать исходный адрес. Интерфейс ввода также не используется в маршрутизации, и после принятия решения о маршрутизации вы не сможете снова переписать адрес назначения.
Итак, вы обнаружите, что это невозможно. У вас есть два варианта. Желательно перенумеровать одну из подсетей с конфликтом сетевых адресов, потому что вы хороший сетевой инженер, а не некомпетентный, и вы создаете сети, которые не являются излишне сложными. Перенумерация подсетей VPN, как правило, несложная задача, которая в худшем случае потребует использования sed
, но, возможно, у вас возникла одна из тех странных ситуаций, когда перенумерация любой подсети является нечеловечески сложной задачей. ХОРОШО.
Если по какой-либо причине вы не можете этого сделать, вам понадобится дополнительный маршрутизатор для работы с DNS с разделением горизонта. Настройте маршрутизатор для каждой подсети (фактически это означает один маршрутизатор для клиентов VPN и один маршрутизатор для хостов в сети, который, к сожалению, использует префикс, который вы выбрали для VPN). Выберите другой диапазон адресов (и это должен быть тот, который в противном случае можно было бы перенумеровать одну из подсетей в ...), чтобы он был «чужим».
Теперь предположим, что мы вызываем сеть со стороной A хостов VPN, а сеть со стороной B хостов, отличных от VPN. Предположим, что на маршрутизаторе A вы выбрали «чужой» префикс 192.0.2.0/24, а на маршрутизаторе B это 198.51.100.0/24. Это будут поддельные префиксы, которые вы будете использовать для хостов в этой подсети для связи с хостами с конфликтующим префиксом в другой подсети (не используйте их, они зарезервированы для целей документации в RFC5737).
Приведенные ниже правила сложны, потому что вы не можете использовать входной интерфейс в качестве предиката в цепочке POSTROUTING, а исходная подсеть не уникальна, поэтому мы должны предотвратить спуфинг с помощью правила фильтрации. Это также сбивает с толку, потому что NETMAP решает, следует ли изменить адрес источника или назначения, в зависимости от того, в какой цепочке он находится.
В маршрутизаторах A и B добавьте правило для входящего трафика по каналу точка-точка между маршрутизаторами, которое я назову ptp0:
router-a # iptables -t nat -A PREROUTING -i ptp0 -d 198.51.100.0/24 -j NETMAP --to 10.0.77.0/24
router-a # iptables -t nat -A POSTROUTING -d 10.0.77.0/24 -j NETMAP --to 192.0.2.0/24
router-a # iptables -t filter -A FORWARD -i \!ptp0 -s 10.0.77.0/24 -j DROP
router-b # iptables -t nat -A PREROUTING -i ptp0 -d 192.0.2.0/24 -j NETMAP --to 10.0.77.0/24
router-b # iptables -t nat -A POSTROUTING -d 10.0.77.0/24 -j NETMAP --to 198.51.100.0/24
router-b # iptables -t filter -A FORWARD -i \!ptp0 -s 10.0.77.0/24 -j DROP
Теперь эта часть проблемы решена, потому что у вас больше нет одного маршрутизатора, который имеет оба префикса в своей таблице маршрутизации. Никакое решение, которое требует, чтобы у вас был один и тот же префикс, относящийся к двум разным сетям с двумя разными хостами на них в одной таблице маршрутизации, не может работать; это невозможно по сути из-за того, как работает маршрутизация.
О, и я почти забыл - я считаю, что ваше правило SNAT не обрабатывается, потому что ничто не соответствует ему, но важно помнить, что после того, как соединение сохраняется в таблице состояний NAT, оно больше не обрабатывается правилом SNAT и нет если я помню, больше засчитывается в статистике.
Если вам на самом деле не требуется, чтобы подсети были разделены, просто используйте мост 2 уровня. Это сделает вашу жизнь намного проще.
Итак, я сделал работу по созданию автономного snat-routing-demo используя Vagrant. В качестве контроля я изначально использовал простой SNAT
, при этом клиенты VPN ссылаются непосредственно на фактический IP-адрес службы, в то время как я уточнил детали настройки OpenVPN и так далее. Затем я ввел виртуальную подсеть и добавил NETMAP
перед SNAT
к столу:
iptables -t nat -A PREROUTING -d <virtual-subnet> -j NETMAP --to <actual-subnet>
который работал. Я думал, что уже пробовал это довольно простое правило в своей реальной среде, но, возможно, я не пробовал, или что-то еще было нарушено; во всяком случае, я могу добиться, чтобы аналогичное правило работало и «по-настоящему». Нет необходимости в использовании знаков, и
iptables -t nat -L -v
теперь показывает, что применяются оба правила.
Правила брандмауэра, ограничивающие, к каким хостам и портам в мостовой подсети можно получить доступ от клиентов VPN, также, похоже, работали без изменений, используя фактические диапазоны IP-адресов, например
iptables -A FORWARD -p tcp -d <actual-host> --dport <some-port> -j ACCEPT
iptables -A FORWARD -s <actual-subnet> -j ACCEPT
iptables -P FORWARD DROP
Я полагаю, что в своем другом ответе я мог несколько неправильно понять ваше намерение, если подумать.
Если я вас правильно понимаю, у вас есть VPN-соединение между двумя сайтами, и он должен маршрутизировать трафик между ними. Но, к сожалению, оба (ранее независимых) сайта в конечном итоге использовали один и тот же адрес RFC1918 для подсети, верно? Это одно из самых неприятных новых свойств RFC1918.
Тем не менее, лучшее решение - изменить нумерацию одной из подсетей. Однако, если вы не можете этого сделать, сопоставление, которое вы пытаетесь сделать, будет неплохим решением.
Однако в ваших правилах есть пара ошибок.
Ваше правило SNAT не регистрирует много трафика, потому что оно не соответствует ничему, кроме собственных пакетов клиентского маршрутизатора openvpn. Адрес источника пакетов, приходящих по каналу VPN, находится в диапазоне 10.0.77.0/24. Таким образом, пакеты проходят через VPN и выходят на другую сторону, но ответы на них отправляются в локальный сегмент, а не обратно через VPN, и, таким образом, теряются.
Во многом так же, как вам нужно создать фиктивный сетевой префикс, чтобы различать адрес назначения в прямом пути, адрес обратного пути тоже должен быть дифференцируемым, что, я думаю, является причиной того, что вы используете SNAT. Итак, вам следует изменить свои правила (и быть более конкретным в отношении того, что вы используете в NETMAP, чтобы не переписывать первые 3 октета каждого отдельного адреса назначения в каждой подсети, проходящей через VPN):
iptables -t mangle -A PREROUTING -i tun0 -j MARK --set-mark 1
iptables -t nat -A PREROUTING -i tun0 -d 10.0.78.0/24 -j NETMAP --to 10.0.77.0/24
iptables -t nat -A POSTROUTING -s 10.0.77.0/24 -m mark --mark 1 -j SNAT --to-source 10.0.77.10
Правило mangle необходимо, потому что вы не можете использовать входной интерфейс в качестве предиката в правиле POSTROUTING, поэтому вам нужен другой способ определить, что пакет пришел из канала VPN и нуждается в NAT источника.