У меня есть машина с двумя интерфейсами, которые имеют разные маршрутизируемые адреса IPv4. Чтобы вернуть трафик на правильный интерфейс, я использовал этот ответ и комментарий, и это работает: я могу подключиться к машине по ssh, используя любой IP-адрес. (Я не знаю, имеет ли это какое-либо влияние, но может.)
Теперь я добавил следующие правила iptables для обратного проксирования / DNAT используя этот ответ. Теперь мой сценарий загрузки выглядит так:
# eth0 is automatically brought up and gets 192.168.1.2 as IP
sudo ip link set eth1 up
sudo ip addr add 192.168.2.2/24 dev eth1
sudo ip rule add from 192.168.2.2 table frometh1
sudo ip route add default via 192.168.2.1 dev eth1 table frometh1
sudo iptables -t nat -A PREROUTING -i eth0 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.1:443
sudo iptables -t nat -A PREROUTING -i eth1 -p tcp -m tcp --dport 443 -j DNAT --to-destination 10.0.0.1:443
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE
echo 1 > /proc/sys/net/ipv4/ip_forward
Первоначально -i eth*
было опущено, а правило DNAT состояло только из одной команды. Правила MASQUERADE также были одной командой путем обмена -o eth*
для -d 10.0.0.1
. Я разделил их, пытаясь сделать интерфейсы явными, на случай, если iptables неявно берет один из интерфейсов.
Когда я пытаюсь достичь 192.168.1.2:443
, трафик перенаправляется и маскируется, как ожидалось. Счетчик попаданий для правильного правила DNAT в таблице PREROUTING увеличивается, а правильное правило MASQUERADE увеличивается. Я могу успешно установить TCP-соединение.
При попытке достичь 192.168.2.2:443
, Я вижу трафик, входящий в интерфейс, и счетчик попаданий для правильного правила DNAT в таблице PREROUTING увеличивается. Но исходящего пакета нет, и ни одно из двух правил MASQUERADE в POSTROUTING не увеличивается. TCP SYN никогда не достигает удаленной системы (10.0.0.1
).
Обратите внимание, что не имеет значения, происходит ли пересылка через eth0 или eth1, можно перенаправить трафик на цель (10.0.0.1
) через eth0 или eth1.
Почему правила iptables работают только для eth0, а не для eth1?
Изменить: может быть важно знать, что проксирование в пользовательском пространстве работает нормально:
mkfifo /tmp/fifo
sudo nc -kvlp 443 </tmp/fifo | nc 10.0.0.1 443 >/tmp/fifo
Позволяет трафику идти в обе стороны, как описано в этом блоге. Проблема с этим решением заключается в том, что удаленный конец видит только одно постоянное TCP-соединение. Он должен видеть одно соединение для каждого клиента и обрабатывать несколько клиентов одновременно.
Сначала нарисуем топологию вашей сети.
Я думаю, что это побочный эффект фильтра обратного пути, потому что пакеты отбрасываются на этапе принятия решения о маршрутизации.
Проверьте вывод nstat -az 'TcpExtIPReversePathFilter'
команда. Скорее всего показывает ненулевой счетчик, который увеличивается при проверках.
Проверьте маршрутизацию командой ip route get 10.0.0.1 from <external-host-ip> iif eth1
. Я думаю это покажет что-то вроде invalid cross-device link
.
Вы можете увидеть текущее состояние rp_filter
на интерфейсах в ip netconf show
вывод команды.
Отключить rp_filter
или лучше установить это в loose
Режим. В rp_filter
предотвращает подделку IP-адресов в нисходящих сетях, что широко используется в атаках с усилением DDoS. В вашем случае настройка не добавляет новых рисков, потому что ваш хост Linux не является основным шлюзом для подключенных сетей.
Это не последняя проблема в вашей настройке. Устранить проблему с rp_filter
если существует, и я расширю ответ, чтобы ваша конфигурация работала.
Теперь, когда rp_filter
проблема решена, давайте попробуем заставить конфигурацию работать должным образом.
Начнем с описания того, что происходит и идет не так.
ext2.IP
. Исходный пакет выглядит какC.IP:<someport> -> <ext2.IP>:443
GW2
и после прохождения через перенаправление портов будет выглядеть, как показано ниже, и будет отправлено на хост linux.C.IP:<someport> -> 192.168.2.2:443
10.0.0.1:443
. Теперь у нас есть пакет в виде:C.IP:<someport> -> 10.0.0.1:443
Только после того, как пункт назначения переписывает, Linux ищет маршрут (решение о маршрутизации). Поскольку исходный адрес в пакетах остается исходным, ваше дополнительное правило маршрутизации не будет задействовано, и пакет будет маршрутизироваться через основную таблицу маршрутизации (я предполагаю, что GW1
будет использоваться). Мы улучшим его, потому что в этом основная причина проблемы.
Пакет отправляется на GW1
через eth0
интерфейс. Исходный адрес будет перезаписан -o eth0 -j MASQUERADE
правило. Пакет будет преобразован в:
192.168.1.2:<otherport> -> 10.0.0.1:443
с MAC-адресом назначения GW1
.
GW1
пересылает этот пакет во внешнюю сеть с дополнительной перезаписью адреса источника.<ext1.IP>:<otherport2> -> 10.0.0.1:443
SYN-ACK
. Ответ будет выглядеть так:10.0.0.1:443 -> <ext1.IP>:<otherport2>
GW1
получает ответ, выполняет обратную трансляцию адреса назначения и отправляет ответ далее на хост linux:10.0.0.1:443 -> 192.168.1.2:<otherport>
SYN-ACK
answer, также выполняет обратный перевод адреса и пытается найти маршрут для дальнейшей пересылки ответа. Но после этого шага что-то пошло не так, но это просто следствие проблемы на шаге 4. Для лучшего понимания нужно проверить набор правил брандмауэра.10.0.0.1:443 -> <C.IP>:<someport>
Основная цель - направить обратно пакеты через тот же интерфейс, на котором эти пакеты были получены. Для этого воспользуемся простыми правилами маршрутизации.
ip route add 192.168.1.0/24 dev eth0 table 10
ip route add 0/0 via 192.168.1.1 dev eth0 table 10
ip route add 192.168.2.0/24 dev eth1 table 11
ip route add 0/0 via 192.168.2.1 dev eth1 table 11
ip rule add iif eth0 lookup 10 pref 1010
ip rule add iif eth1 lookup 11 pref 1011
Все пакеты, полученные на конкретном интерфейсе, будут маршрутизироваться по ограниченной таблице маршрутизации, в которой есть маршруты только для одного интерфейса. Это простейшее решение, но оно устраняет возможность хоста Linux пересылать пакеты между интерфейсами (из eth0
к eth1
и наоборот). Если это вам не подходит, есть другой способ, но более сложный. Опишу, если нужно.