iptables - пересылка входящего трафика на внутренний IP-адрес (интерфейс докера)

Концепция дескриптора файла важна из-за выбора дизайна UNIX, согласно которому «все является файлом», включая вещи, не являющиеся частью файловой системы. Такие как ленточные накопители, клавиатура и экран (или телетайп!), Устройства чтения перфокарт / лент, последовательные соединения, сетевые соединения и (ключевое изобретение UNIX) прямые соединения с другими программами, называемые «каналами».

Если вы посмотрите на многие простые стандартные утилиты UNIX, такие как grep , особенно в их исходных версиях, вы заметите, что они не включают вызовы к open () и close () , но просто читать и писать . Дескрипторы файлов устанавливаются вне программы оболочкой и передаются при ее запуске. Таким образом, программе не нужно заботиться о том, пишет ли она в файл или в другую программу.

Помимо open , другие способы получения файловых дескрипторов: socket , listen , pipe , dup и очень удобный механизм Хита Робинсона для отправки дескрипторов файлов по каналам: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux-socket

Изменить: некоторые лекции , описывающие уровни косвенного обращения и то, как это позволяет O_APPEND работать разумно. Обратите внимание, что сохранение данных inode в памяти гарантирует, что системе не придется снова их извлекать для следующей операции записи.

2
27.02.2019, 10:54
1 ответ

Есть две проблемы (и на самом деле не -задал 3-й вопрос, который я рассмотрю простым, если не лучшим решением, на всякий случай, чтобы быть тщательным):

Локально инициированные пакеты не пересылаются/маршрутизируются

Локально инициированные пакеты не пересылаются (не маршрутизируются ). Таким образом, эти пакеты никогда не видят цепочку nat/PREROUTING. Взгляните на Поток пакетов в Netfilter и General Networking , чтобы получить представление о том, что происходит во время жизни пакета в ядре. Локальные пакеты приходят из "локального процесса".

Таким образом, в дополнение к правилу nat/PREROUTINGвыполняется DNATдля пакетов, прибывающих "извне", что должно выглядеть как:

iptables -t nat -I PREROUTING -i eno1 -p tcp --dport 8443 -j DNAT --to-destination 172.17.0.2:8443

Вы также должны использовать цепочку nat/OUTPUT. На выходе его синтаксис допускает только исходящие интерфейсы, поэтому он изменен следующим образом:

iptables -t nat -I OUTPUT -o lo -p tcp --dport 8443 -j DNAT --to-destination 172.17.0.2:8443

Исходный пакет, а затем поток будут фактически перенаправлены на другой интерфейс (Я подозреваю, что "проверка перенаправления" на схеме предыдущей ссылки может быть размещена неправильно ).

Это будет работать с любым IP-адресом, принадлежащим хосту (, т.е. :172.16.214.45 и 172.17.0.1 ), кроме...

диапазон IP 127.0.0.0/8 запрещен для просмотра за пределами интерфейса lo

Ядро Linux имеет специальные настройки, запрещающие маршрутизацию любого IP-адреса в диапазоне 127.0.0.0/8 куда-либо, кроме интерфейса lo, и отбрасывает любой такой пакет как марсианский источник при «попытке» использовать другой интерфейс, и правильно :удаленная система (даже если это контейнер )не примет входящий пакет с источником 127.0.0.1 и пунктом назначения 172.17.0.2 хотя бы потому, что не будет знать, куда на него ответить.

Таким образом,SNAT(или простойMASQUERADE)к пакету в дополнение к DNATтакже необходимо сделать, на этот раз в цепочке nat/POSTROUTING, которая проходится (см. предыдущую схему):

iptables -t nat -I POSTROUTING -s 127.0.0.1 -d 172.17.0.2 -j MASQUERADE

Этого еще недостаточно :как следует из названия, nat/POSTROUTINGпроисходит после маршрутизации (на самом деле проверка перемаршрутизации происходит после DNAT), и пакет уже был отброшен как марсианский источник.

В особых случаях, таких как этот, можно переопределить ограничение локальной сети с помощью переключателя интерфейса -route_localnet:

echo 1 > /proc/sys/net/ipv4/conf/docker0/route_localnet

Теперь стек маршрутизации пропускает пакеты с источником 127.0.0.1, а их источник корректируется на 172.17.0.1 по предыдущему правилу перед выходом по виртуальному проводу в контейнер :это работает.

Вам действительно следует избегать всего, что требует этого второго случая, потому что это ненужная сложность :использование IP-адреса, принадлежащего хосту, а не 127.0.0.1, должно быть достаточно для любого теста. Кроме того, если интерфейс docker0будет удален и создан заново, настройка route_localnetбудет потеряна, и было бы неразумно устанавливать ее по умолчанию.

Шпильки

Не задано, но если вы добавите сюда вторую систему (контейнер )в той же локальной сети, возникнут проблемы с локальной сетью -на -хост -на -тот же -перенаправления локальной сети (, если только Docker уже не обрабатывает это на сетевом уровне ).

Правило nat/PREROUTING, которое я написал в начале ответа, обрабатывает только интерфейс eno1. Была причина, по которой я добавил это -i eno1ограничение :без него, если другой контейнер в сети 172.17.0.0/16 попытается подключиться, например, к 172.16.214.45 :8443 (или к 172.17.0.1 :8443 ), пакет будет перенаправлен на 172.17.0.2. Затем 172.17.0.2 ответит напрямую источнику :другому контейнеру и полностью обойдет хост и его правила NAT.Этот контейнер увидит ответный пакет, пришедший из источника, о котором он не знает, и отклонит его (с помощьюTCP RST). Так что лучше не справляться с этим вообще, чем справляться плохо. Docker, вероятно, предоставляет определенные способы прямого разрешения службы на IP/порт другого контейнера без участия хоста.

В любом случае, если это необходимо, существует несколько способов преодолеть это, часто с компромиссом, от простого NAT (, который теряет исходный IP-адрес или должен транслировать его в фиктивную сеть, для целей регистрации )до сложного моста и /или настройки маршрутизатора, способные перехватывать связь по локальной сети.

Вот простое решение, где источником является SNAT -ed, использующий NETMAP, для фиктивной сети 10.17.0.0/16. Простое предварительное условие :10.17.0.0/16, вероятно, должно быть направлено на хост (, даже если он на самом деле не используется ), либо по маршруту по умолчанию (, вероятно, в случае ), определенному маршруту, либо с помощью хост, имеющий IP-адрес в этой фиктивной сети для этой цели. Пакеты с этим IP-адресом будут существовать только внутри сети docker0.

После удаления -i eno1из правила PREROUTINGвыше добавьте это новое правило:

iptables -t nat -I POSTROUTING -s 172.17.0.0/16 -d 172.17.0.0/16 -j NETMAP --to 10.17.0.0/16

Теперь перенаправление из локальной сети в ту же локальную сеть будет работать, а журналы контейнера назначения будут показывать исходные IP-адреса в диапазоне 10.17.0.0/16.

Конечно, следует также избегать ситуаций, связанных со шпильками.

1
27.01.2020, 22:27

Теги

Похожие вопросы