Как перенаправить трафик из контейнера А в контейнер Б при использовании адреса внешнего интерфейса
Следуя краткой/упрощенной схеме моей установки (извините за заголовок):
Internet ----- eth0 (1.2.3.4) --- br0 (10.0.0.1)
|
+---------------+---------------+
| |
veth0 (10.0.0.2) veth1 (10.0.0.3)
httpd app
Я перенаправляю внешний трафик через обычный DNAT/SNAT (с использованием nftables) от eth0 к подходящей службе (например, к , если кто-то обращается к порту 443 на eth0). Я также могу общаться между контейнерами (например, из в ) или между контейнерами и «хостом» (через br0), если они используют внутренние IP-адреса (10.0.0.X). Все работает без включенияroute_localnet
.
Кажется, я не могу заставить себя работать, если, например, контейнер пытается связаться с использованием внешнего адреса (1.2.3.4). Например, если отправляется запрос на адрес 1.2.3.4:443. То, что он использует 1.2.3.4, а не внутренний 10.0.0.2, находится вне моего контроля (я мог бы исправить некоторые случаи, добавив представление разделенного DNS, но, конечно, не все, так что оно того не стоит).
Когда я пытаюсь поговорить из контейнера с использованием внешнего адреса, например, с помощью socat (socat -4 - TCP4:1.2.3.4:443
), я получаю EHOSTUNREACH («Нет маршрута к хосту»).
Я попытался добавить отдельный DNAT для этого случая, например:nft add rule ip contBr prerouting iif "br0" ip daddr 1.2.3.4 tcp dport { 80, 443 } dnat to 10.0.0.2
. Идея заключалась в том, что, поскольку он меняет адрес назначения только перед маршрутизацией, он будет знать, куда отправить его во время маршрутизации, а исходный адрес остается 10.0.0.3, поэтому возврат ответа должен быть в порядке. Однако не работает. Теперь при попытке поговорить сhttpd
отapp
, я получаю ETIMEDOUT («Тайм-аут соединения истек»), что не должно быть для меня каким-либо фильтром, поскольку я отклоняю все, что мне не нужно, а не просто удаляю.
Не знаю, что именно искать в Интернете или здесь.
1 ответ
После полезного комментария Престона на мой вопрос я узнал, что термин/метод, который я ищу, называетсяHairpinning
/Hairpin NAT
( https://en.wikipedia.org/wiki/Hairpinning , https://newspaint.wordpress.com/2018/09/13/hairpin-for-lxc-containers-using-iptables/).
Причина, по которой мое единственное правилоnft add rule ip contBr prerouting iif "br0" ip daddr 1.2.3.4 tcp dport { 80, 443 } dnat to 10.0.0.2
не сработало, заключается в том, что при изменении адреса назначения на правильный ответ тоже пойдет обратно к ожидаемому источнику (, 10.0.0.3); запущенное приложение будет ожидать ответа от хоста с адресом 1.2.3.4, а не 10.0.0.2 - но это произойдет из-за DNAT.
Решение - пометить соединение, и при отправке ответа обратно наapp
нам нужно либо добавить SNAT, либо MASQUERADE; чтобы пакет ответов получил ожидаемый адрес источника 1.2.3.4.
Что работает для меня:
nft add rule ip contBr prerouting iif "br0" ip daddr 1.2.3.4 tcp dport { 80, 443 } ct mark set 0x61687269
nft add rule ip contBr prerouting iif "br0" ip daddr 1.2.3.4 tcp dport { 80, 443 } dnat to 10.0.0.2
nft add rule ip contBr postrouting oif "br0" ct mark 0x61687269 snat to 1.2.3.4
/------------------------------------\
/ \
RESP 10.0.0.3 / REQ 1.2.3.4:443 \
SNAT 1.2.3.4 / SRC 10.0.0.3 \
| /------------------------------------\ \
| | \ |
Internet ----- eth0 (1.2.3.4) --- br0 (10.0.0.1) | |
| | | | |
| | DNAT 10.0.0.2 | | |
| | SRC 10.0.0.3 | | |
| | | | |
RESP 10.0.0.3 | | +---------------+---------------+ | |
SRC 10.0.0.2 | | | | | |
| | | | | |
| | | | | |
^ v | | ^ v
veth0 (10.0.0.2) veth1 (10.0.0.3)
httpd app