Tcpdump показывает другой порт перенаправления после добавления правила REDIRECT в iptables

Tcpdump показывает другой порт перенаправления после добавления правила REDIRECT в iptables

Я пытаюсь направить клиентский трафик в кластер Kubernetes NodePort, прослушивающий 192.168.1.100.30000.

Клиенту необходимо сделать запрос, 192.168.1.100.8000поэтому я добавил следующее правило REDIRECT в iptables:

iptables -t nat -I PREROUTING -p tcp --dst 192.168.1.100 --dport 8000 -j REDIRECT --to-port 30000

Затем я запускаю curl 192.168.1.100:8000, однако в tcpdump я вижу другой порт:

# tcpdump -i lo -nnvvv host 192.168.1.100 and port 8000
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
[Interface: lo] 20:39:22.685968 IP (tos 0x0, ttl 64, id 20590, offset 0, flags [DF], proto TCP (6), length 40)
[Interface: lo]     192.168.1.100.8000 > 192.168.1.100.49816: Flags [R.], cksum 0xacda (correct), seq 0, ack 3840205844, win 0, length 0
[Interface: lo] 20:39:37.519256 IP (tos 0x0, ttl 64, id 34221, offset 0, flags [DF], proto TCP (6), length 40)

Я ожидаю, что tcpdump покажет что-то вроде

192.168.1.100.8000 > 192.168.1.100.30000

Однако он отображается и вызывает ошибку «Отказано в подключении», поскольку ни один процесс не указан 192.168.1.100.49816.

192.168.1.100.8000 > 192.168.1.100.49816

Я использую тестовую среду, поэтому у меня нет доступа к удаленным устройствам, поэтому я использую curlдля тестирования путь iptables REDIRECT.

Есть ли причина, по которой добавление правила REDIRECT заставляет tcpdump перенаправлять трафик на порт, отличный от указанного?

Редактировать:

После предложения @AB добавлено следующее правило ВЫХОДА:

iptables -t nat -I OUTPUT -d 192.168.1.100 -p tcp --dport 8000 -j REDIRECT --to-port 30000

и curl продолжает работу, количество пакетов для цепочки OUTPUT увеличивается (хотя количество пакетов для цепочки PREROUTING REDIRECT не увеличивается):

2       10   600 REDIRECT   tcp  --  *      *       0.0.0.0/0            192.168.1.100         tcp dpt:8000 redir ports 30000

Однако возникает следующая ошибка:

# curl -vk https://192.168.1.100:8000/v1/api
* About to connect() to 192.168.1.100 port 8000 (#0)
*   Trying 192.168.1.100...
* Connected to 192.168.1.100 (192.168.1.100) port 8000 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* NSS error -12263 (SSL_ERROR_RX_RECORD_TOO_LONG)
* SSL received a record that exceeded the maximum permissible length.
* Closing connection 0
curl: (35) SSL received a record that exceeded the maximum permissible length.

Также попробовал добавить сеть remotesystem, на этот раз количество пакетов PREROUTING REDIRECT CHAIN ​​увеличивается после выполнения remotesystem curl ...(но OUTPUT CHAIN ​​не увеличивается):

2       34  2040 REDIRECT   tcp  --  *      *       0.0.0.0/0            172.16.128.1         tcp dpt:8000 redir ports 30000

Ошибка:

# ip netns exec remotesystem curl -vk https://192.168.1.100:8000/v1/api
* About to connect() to 192.168.1.100 port 8000 (#0)
*   Trying 192.168.1.100...
* Connection timed out
* Failed connect to 192.168.1.100:8000; Connection timed out
* Closing connection 0
curl: (7) Failed connect to 192.168.1.100:8000; Connection timed out

решение1

Для ясности: тест OP выполняется из системы 192.168.1.100 к себе, а не из удаленной системы, и это причина проблемы. В этом случае порт не был изменен, поскольку не совпало ни одно правило NAT, хотя он бы совпал, если бы был выполнен из удаленной системы.

На схеме ниже показан порядок выполнения операций над пакетом:

Поток пакетов в Netfilter и общих сетях

Причина в том, как работает NAT в Linux:iptablesвидит пакет в natтаблице только для первого пакета нового потока conntrack (который, таким образом, находится в состоянии NEW).

Это правило отлично работает с удаленной системы. В этом случае первый увиденный пакет будет входящим пакетом:

to port 8000 --> AF_PACKET (tcpdump) --> conntrack --> nat/PREROUTING (iptables REDIRECT): to port 30000
--> routing decision --> ... --> local process receiving on port 30000

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

to port 8000 --> AF_PACKET (tcpdump) --> conntrack: to port 30000
--> routing decision --> ... --> local process receiving on port 30000

Для теста на самой системе первый пакет не входящий, а исходящий. Это происходит вместо этого, используя исходящий loинтерфейс:

local process client curl --> routing decision --> conntrack --> nat/OUTPUT (no rule here)
--> reroute check --> AF_PACKET (tcpdump) --> to port 8000

И теперь этот пакет возвращается обратно на loинтерфейс, он снова появляется как пакет, который больше не является первым пакетом в соединении, поэтому следует второму случаю, как указано выше: conntrack самостоятельно заботится о NAT и не вызывает nat/PREROUTING. За исключением того, что на предыдущем шаге ему не было указано выполнять какой-либо NAT:

to port 8000 --> AF_PACKET (tcpdump) --> conntrack
--> routing decision --> ... -->nolocal process receiving on port8000

Поскольку порт 8000 никто не прослушивает, ОС отправляет обратно TCP RST.

Чтобы это работало в локальной системе, REDIRECTв цепочку также необходимо добавить правило nat/OUTPUT:

iptables -t nat -I OUTPUT -d 192.168.1.100 -p tcp --dport 8000 -j REDIRECT --to-port 30000

Дополнительные замечания

  • если случай предназначен для удаленного использования, не тестируйте из локальной системы: правила, пройденные тестом, не совпадают. Это делает тест не отражающим реальность.

    Просто используйте сетевое пространство имен для создания карманной удаленной системы в случае, если нет другой доступной системы. Пример, который должен работать с системой, имеющей только nat/PREROUTINGправило OP и выполняющей curl http://192.168.1.100/(что не требует DNS):

    ip netns add remotesystem
    ip link add name vethremote up type veth peer netns remotesystem name eth0
    ip address add 192.0.2.1/24 dev vethremote
    ip -n remotesystem address add 192.0.2.2/24 dev eth0
    ip -n remotesystem link set eth0 up
    ip -n remotesystem route add 192.168.1.100 via 192.0.2.1
    ip netns exec remotesystem curl http://192.168.1.100:8000/
    
  • tcpdumpи НАТ

    tcpdumpпроисходит на AF_PACKETэтапах в схеме выше: очень рано для входа и очень поздно для выхода. Это означает, что в случае удаленной системы он никогда не захватит порт 30000, даже если он работает. В случае локальной системы, как только правило nat/OUTPUTдобавлено, он захватит порт 30000.

    Только не доверяйте слепо адресу/порту, отображаемому при tcpdumpвыполнении NAT: это зависит от случая и того, где происходит перехват.

Связанный контент