
Я пытаюсь направить клиентский трафик в кластер 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, хотя он бы совпал, если бы был выполнен из удаленной системы.
На схеме ниже показан порядок выполнения операций над пакетом:
Причина в том, как работает 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 --> ... -->
no
local process receiving on port
8000
Поскольку порт 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: это зависит от случая и того, где происходит перехват.