Подробно о docker и iptables
После предыдущего поста у меня в голове не совсем укладывалось, как именно идут пакеты в контейнеры и обратно. Почему они проходят именно такие цепочки и именно в такой последовательности?
Я разложил для себя все по полочкам и собрал в статью. Получилось несколько нудно, но может быть такая детализация будет кому-то полезна.
Пост больше о работе iptables на роутере.
Итак, как же проходит трафик через iptables в контейнеры докера и из контейнеров докера? Как докер модифицирует iptables, чтобы вся система работала?
Ментальная модель
Самая простая для понимания ментальная модель такая. Представьте, что контейнеры докера - это отдельные компьютеры во внутренней сети. Каждый из них имеет свой внутренний айпишник (обычно 172.17.0.Х). А Docker Host - это компьютер, соединяющий внутреннюю сеть с интернетом.
Поскольку наш Docker Host подключен к двум сетям, у него есть две "сетевые карты", два сетевых интерфейса - eno1 подключен к внешней сети, а docker0 - ко внутренней.
Конечно, в вашем случае названия интерфейсов и их количество могут различаться, но принцип не меняется.
И вот тут самое главное: комьютер Docker Host работает как роутер для "компьютеров" внутренней сети. Он пересылает пакеты с одной сетевой карты на другую, а пути делает разные модификации, чтобы все работало - MASQUERADE, DNAT, SNAT.
Мы не будем погружаться в механизмы трансляции адресов (nat). Для нас важно понять только тот факт, что весь трафик в конейтеры и обратно является FORWARD-трафиком. То есть, он не адресован компьютеру, на котором запущен демон докера.
А где фильтруется пересылаемый трафик?
Фильтрация трафика в iptables
iptables так устроен, что правила фильтрации трафика прописываются только в одном месте. Это очень удобно и очень просто.
Точнее таких места два - цепочка INPUT и цепочка FORWARD. Но они не пересекаются.
В цепочку INPUT попадают пакеты, адресованные локальным программам.
В цепочку FORWARD - пакеты, которые пересылаются.
Поскольку контейнеры докера - это отдельные компьютеры во внутренней сети, как мы помним из нашей ментальной модели ☝️, то весь трафик в контейнеры докера проходит только цепочку FORWARD.
Давайте посмотрим, какие именно правила добавляет докер в таблицу FORWARD:
$ sudo iptables -S FORWARD
-P FORWARD DROP
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -o docker0 -j DOCKER
-A FORWARD -i docker0 ! -o docker0 -j ACCEPT
-A FORWARD -i docker0 -o docker0 -j ACCEPTПользовательские правила располагаются в цепочке DOCKER-USER. Если вы в нее ничего не добавили, то там нет никакой фильтрации. О настройке этой цепочки читайте мою статью: как настроить пользовательские правила iptables для docker.
Трафик из контейнеров матчится правилами -i docker0. Помните, мы разбирали путь пакетов выше? Входят через интерфейс docker0, а выходят через какой-то другой во внешнюю сеть.
container -> -i docker0 -o eno1 -> internetТрафик в конейнеры попадает под действие правил -o docker0.
internet -> -i eno1 -o docker0 -> containerПосмотрим несколько примеров, чтобы вы понимали, какие конкретно правила проходят пакеты.
Пакеты из контейнеров наружу
Пакеты, которые идут из контейнеров во внешний мир, никак не задерживаются благодаря вот этому правилу:
-A FORWARD -i docker0 ! -o docker0 -j ACCEPTТо есть любые исходящие соединения разрешены без каких-либо ограничений.
Пакеты из интернета в контейнеры на опубликованные порты
Если мы опубликуем порт, то в цепочку DOCKER таблицы filter добавится новое правило. Например, я запустил контейнер nginx и опубликовал порт -p 7777:80. Добавилось новое правило:
$ sudo iptables -S DOCKER
-N DOCKER
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 80 -j ACCEPTТо есть пропускать на порт 80 --dport 80 все пакеты, которые идут в контейнеры -o docker0 в конкретный контейнер с айпи -d 172.17.0.2
Пакеты из интернета в контейнеры по ответным соединениям
Допустим, мы изнутри контейнера пингуем гугл. Исходящие пакеты не задерживаются, это мы уже разобрали выше:
# сначала проходит цепочку (она пустая)
-A FORWARD -j DOCKER-USER
# потом еще какую-то (она тоже ничего не делает)
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
# исходящий пакет разрешен к передаче
-A FORWARD -i docker0 ! -o docker0 -j ACCEPTНо когда от гугла приходит ответный пакет, как он попадает в систему? Вот так:
# так же проходит две цепочки
-A FORWARD -j DOCKER-USER
-A FORWARD -j DOCKER-ISOLATION-STAGE-1
# дальше по цепочке DOCKER, но там нет матча
-A FORWARD -o docker0 -j DOCKER
# и в конце срабатывает вот это правило:
-A FORWARD -o docker0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
Поскольку ответ на пинг является RELATED-соединением, он пропускается.
Люблю IPTABLES
When planning a cosplay look, it is worth considering how details related to men's cosplay costumes will affect the complete look. When comparing costume options, a comparison of details related to long cosplay wigs can clarify differences in colour and construction. To compare practical details about details related to cosplay shoes, short cosplay wigs with delivery information can help narrow the options for a specific purpose. To reuse the pieces at future events, a balanced view of details related to cosplay shoes can support both detail and comfort.
P. S. Сейчас подумал, что не очень эффективно дублировать все эти правила в DOCKER-USER, как сделано в предыдущей статье. Может быть будет проще дописать туда только явно запрещающие входящие правила - вроде "запрещать входящий трафик в контейнеры на любые порты, кроме 80, 443, ХХХ". Результат!