Назад | Перейти на главную страницу

iptables Source NAT с несколькими сетевыми картами и общедоступными IP-адресами не работает

У меня просто проблема на сервере с несколькими сетевыми картами и IP-адресами. Я настроил свой сервер Cent OS 7 с дополнительным общедоступным IP-адресом, принадлежащим eth1. Когда iptables source NAT включен, нельзя использовать только дополнительный IP-адрес.

В этом посте два общедоступных IP-адреса обозначены 99.0.1.100 (eth0) и 99.0.2.100 (eth1). Маска подсети 99.0.2.100 - 255.255.254.0, шлюз по умолчанию установлен правильно (99.0.1.1 для 99.0.1.100, 99.0.2.1 для 99.0.2.100).


ниже содержание /etc/sysconfig/network-scripts/ifcfg-eth1. (HWADDR подвергается цензуре)

DEVICE=eth1
TYPE=Ethernet
HWADDR=**:**:**:**:**:**
ONBOOT=yes
NM_CONTROLLED=no
BOOTPROTO=static
IPADDR=99.0.2.100
NETMASK=255.255.254.0

ниже вывод ip route.

default via 99.0.1.1 dev eth0 proto static metric 100
99.0.1.0/23 dev eth0 proto kernel scope link src 99.0.1.100 metric 100
99.0.2.0/23 dev eth1 proto kernel scope link src 99.0.2.100

ниже вывод ip rule.

0:      from all lookup local
32765:  from 99.0.2.100 lookup subroute-eth1
32766:  from all lookup main
32767:  from all lookup default

ниже содержание /etc/sysconfig/network-scripts/route-eth1.

default via 99.0.2.1 table subroute-eth1

ниже содержание /etc/sysconfig/network-scripts/rule-eth1.

from 99.0.2.100 table subroute-eth1

затем следующий запрос был отправлен правильно и ответил правильный IP-адрес в виде строки для каждого общедоступного IP-адреса.

# curl --interface eth0 api.ipify.org  # outputs 99.0.1.100
# curl --interface eth1 api.ipify.org  # outputs 99.0.2.100

здесь я хочу, чтобы каждый пользователь системы использовал свой публичный IP-адрес. Итак, применил следующий исходный NAT iptables. Где uid = 1000 - это A, uid = 1001 - это B соответственно.

# iptables -t nat -m owner --uid-owner 1000 -A POSTROUTING -j SNAT --to-source 99.0.1.100
# iptables -t nat -m owner --uid-owner 1001 -A POSTROUTING -j SNAT --to-source 99.0.2.100

в этой ситуации eth0 работает правильно, но время ожидания eth1 истекло.

# curl api.ipify.org  # outputs 99.0.1.100
# sudo -u A curl api.ipify.org  # outputs 99.0.1.100
# sudo -u B curl api.ipify.org  # **time out**

Я понятия не имею, что не так, из-за отсутствия опыта работы в сети. Если у кого-то есть идеи, помогите, пожалуйста. Обратите внимание, что в этой ситуации ssh root@99.0.2.100 из Интернета все еще работает нормально.


редактировать: ниже вывод ip route show table subroute-eth1

default via 99.0.2.1 dev eth1

Uid должен вызвать изменение маршрутизации для использования eth1 вместо того eth0. nat/POSTROUTINGSNAT (хотя он все еще необходим) приходит слишком поздно для этого, потому что, как следует из названия POSTROUTING, это происходит после решения о маршрутизации: сетевой интерфейс выбран и больше не будет меняться.

Для локального трафика правило должно выполняться в mangle/OUTPUT цепь, чтобы изменить маршрут, используя метки, которые могут вызвать ip rule уважать:

# iptables -t mangle -A OUTPUT -m owner --uid-owner 1000 -j MARK --set-mark 1000
# iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 1001

Затем информация повторно используется через ip rule. Следует учитывать даже 1000 (для конкретного случая uid1000$ curl --interface eth1 http://api.ipify.org/ который не должен использовать запись 32765):

ip rule add fwmark 1000 lookup main
ip rule add fwmark 1001 lookup subroute-eth1

Обратите внимание, что исходный IP-адрес по умолчанию был выбран раньше: SNAT все еще необходим, чтобы исправить его на новый правильный. Зачем использовать этот знак, если уже есть правило с from 99.0.2.100 lookup subroute-eth1 затем? Опять же, это из-за порядка различных вычислений в сетевом стеке, как показано ниже. Поток пакетов в схемах Netfilter и General Networking: только изменение mangle/OUTPUT может вызвать проверка маршрута видно на схеме, и к этому времени IP не был исправлен до 99.0.2.100.

В обратном пути некоторые элементы сети все равно не узнают об этих таблицах маршрутизации вовремя, необходимо установить свободный режим. eth1с rp_filter или возвратные пакеты будут отброшены:

# echo 2 > /proc/sys/net/ipv4/conf/eth1/rp_filter

То же самое для eth0 для обработки конкретного случая для uid 1000 выше (uid1000$ curl --interface eth1 http://api.ipify.org/):

# echo 2 > /proc/sys/net/ipv4/conf/eth0/rp_filter

При этом следует в основном работай. Но на самом деле несколько пакетов не принадлежат uid 1001: TCP RST, последний ACK после завершения процесса или для UDP связанные ошибки ICMP ... принадлежат только ядру. Они не будут соответствовать uid 1001, будут отправлены на неправильный интерфейс (с неправильным IP-адресом на этом неправильном интерфейсе), и это приведет к возникновению тайм-аутов в конце соединений, более вероятно, на удаленной стороне, а не локально. Это можно увидеть, выполнив kill -KILL на клиенте с измененным соединением с пользователем 1001 через eth1, и наблюдаем несколько повторных попыток ACK пакеты с источником 99.0.2.100, отправленные через eth0 (вместо того eth1) признавая FIN с пульта получено несколько раз на eth1.

Так что все это нужно переработать с помощью CONNMARK выслеживать потоки инициированный пользователем, вместо пакеты инициирован пользователем (некоторые пояснения Вот). nat не нужно менять, так как только 1-й пакет в NEW state все равно проходит через правила iptables и уже отслеживает потоки. Давай начнем сначала:

# iptables -t mangle -F
# iptables -t mangle -A PREROUTING -j CONNMARK --restore-mark
# iptables -t mangle -A OUTPUT -j CONNMARK --restore-mark
# iptables -t mangle -A OUTPUT -m owner --uid-owner 1000 -j MARK --set-mark 1000
# iptables -t mangle -A OUTPUT -m owner --uid-owner 1001 -j MARK --set-mark 1001
# iptables -t mangle -A OUTPUT -j CONNMARK --save-mark

Теперь все должно вести себя правильно.

Последнее замечание: хотя в ваших настройках этого не требовалось, локальный маршрут ссылки для eth1 должен быть продублирован в таблице. subroute-eth1, иначе доступ eth1LAN из uid 1001 даст странные результаты, потому что правило 32765 читается перед правилом 32766, поэтому локальный маршрут ссылки никогда не будет определен, поскольку результат этой команды показывает:

# ip route get 99.0.2.55 mark 1001
99.0.2.55 via 99.0.2.1 dev eth1 table subroute-eth1 src 99.0.2.100 mark 0x3e9 uid 0 \    cache

Это может предотвратить доступ ко всей ЛВС, кроме шлюза.

Вы должны добавить (или поместить, если необходимо, в файлы конфигурации системы):

ip route add table subroute-eth1 99.0.2.0/23 dev eth1 scope link src 99.0.2.100

А потом предыдущий ip route get команда выдаст вместо этого правильный:

99.0.2.55 dev eth1 table subroute-eth1 src 99.0.2.100 mark 0x3e9 uid 0 \    cache