Зачем вообще говорить о Docker и файрволе
Docker упрощает жизнь разработчикам и админам, но вместе с удобством приходит и скрытая автоматика. Демон добавляет и удаляет правила в iptables или через совместимость — в nftables. Это влияет на FORWARD, INPUT, NAT и может неожиданно открыть лишние пути между контейнерами, между мостами, наружу и на сам хост. Без четкой модели безопасности вы рискуете либо поломать сеть контейнеров, либо оставить «дырки» в обороне.
Ниже — практическая методичка: как работает цепочка DOCKER-USER, какие политики задать по умолчанию, как изолировать bridge-сети, аккуратно настроить NAT для исходящего трафика и опубликовать порты без сюрпризов. Примеры — и для iptables, и для nftables, с подсказками по sysctl и типичным взаимодействиям с UFW и firewalld. Если готовите хост под прод — удобнее сразу строить периметр на изолированном VDS.
Как Docker меняет таблицы: что появится в системе
При запуске контейнеров Docker создаёт и обновляет несколько цепочек. Для варианта с iptables вы увидите цепочки DOCKER, DOCKER-ISOLATION-STAGE-1, DOCKER-ISOLATION-STAGE-2, DOCKER-USER и правила для MASQUERADE в таблице nat. Цепочка DOCKER-USER — специально для ваших правил: трафик проходит через неё раньше, чем попадёт в DOCKER, поэтому здесь удобно вводить общесистемные ограничения.
Если хост использует «совместимость» iptables поверх nft (iptables-nft), Docker создаёт объекты в семействе ip таблиц filter и nat внутри общего ruleset nftables. Это важно: вы можете добавлять свои собственные nft-цепочки с приоритетом выше или ниже докеровских, чтобы управлять порядком обработки.
Быстрый осмотр:
iptables -Sиiptables -t nat -Sдля iptables;nft list rulesetдля nftables. Для трассировки в nft используйтеnft monitor trace.

Базовая модель: «закрыто по умолчанию», stateful-исключения и DOCKER-USER
Надежная схема — минимально открытые политики на INPUT и FORWARD плюс точечные разрешения. Docker сам добавляет правила, чтобы работали опубликованные порты и межконтейнерный обмен в пределах сети. Наша задача — ограничить лишнее, не ломая легитимные сценарии.
iptables: безопасный каркас
Последовательность действий должна быть аккуратной, чтобы не потерять SSH. Сначала разрешаем нужное, затем опускаем политики.
# Разрешаем уже установленные соединения
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Локалку
iptables -A INPUT -i lo -j ACCEPT
# SSH (подставьте свой порт)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# Политика по умолчанию
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Цепочка DOCKER-USER обрабатывается раньше DOCKER
# Разрешаем обратный трафик
iptables -A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# Запрещаем прямой трафик между docker bridge-интерфейсами (изоляция мостов)
iptables -A DOCKER-USER -i br+ -o br+ -j DROP
# Возвращаемся к стандартной обработке (цепочка DOCKER)
iptables -A DOCKER-USER -j RETURN
Такой каркас оставляет опубликованные порты рабочими (их примет цепочка DOCKER), но блокирует прямую связность между мостами Docker. Если в пределах одного моста нужна полная связность контейнеров — она сохранится, правило с br+ ограничивает именно межмостовую пересылку.
nftables: эквивалентный каркас
В nftables удобно вынести свои правила в отдельную таблицу с более высоким приоритетом, чем iptables-независимые цепочки Docker. Ниже — пример. Приоритет -200 гарантирует обработку раньше «iptables-nft» цепей (у них обычно priority 0).
table inet fastfox {
chain input {
type filter hook input priority -200;
ct state established,related accept
iifname "lo" accept
tcp dport { 22 } accept
counter drop
}
chain forward {
type filter hook forward priority -200;
ct state established,related accept
iifname "br+" oifname "br+" drop
counter accept
}
}
# NAT для исходящего (если нужен общий исход во внешку)
table ip nat_out {
chain postrouting {
type nat hook postrouting priority 100;
oifname "eth0" masquerade
}
}
Цепочка forward допускает трафик по умолчанию после нашего раннего дропа для межмостовой связности, а дальше уже подключатся правила, которые Docker добавит через iptables-совместимость. Если нужна более строгая модель — оставьте в конце не accept, а drop и явно разрешайте направления (см. ниже раздел про egress-фильтрацию).
Бриджи Docker: как изолировать сети правильно
По умолчанию Docker создаёт docker0 и user-defined bridge-сети br-* для compose-проектов. Контейнеры внутри одной такой сети связаны, а между разными сетями — изолированы на уровне iptables правил Docker. Однако межмостовая связность может появиться, если вы вручную включаете --link, публикуете порты и прокладываете маршруты. Лучше закрепить изоляцию явным правилом, как выше: -i br+ -o br+ -j DROP в DOCKER-USER или соответствующее правило в nft.
Ещё один надёжный приём — отключить дефолтный мост и создавать сети явно. Это упрощает контроль адресных пространств и политик.
# /etc/docker/daemon.json
{
"bridge": "none",
"iptables": true,
"default-address-pools": [
{"base": "172.20.0.0/16", "size": 24}
]
}
Дальше создавайте сети для каждого проекта отдельно, избегайте пересечений подсетей, и назначайте контейнеры только в нужные сети. Если нужен «внутренний» сегмент без выхода в интернет, создавайте сеть с флагом internal (в compose — internal: true): такая сеть не будет маскарадуваться наружу.
Тонкая настройка межконтейнерной связности: в старых конфигурациях встречается опция
icc(inter-container communication). Для современных user-defined сетей разумнее опираться на сетевые ACL на уровне файрвола и избирательно публиковать порты.
NAT и egress-контроль: выпускаем контейнеры наружу безопасно
Маскарадинг (MASQUERADE) нужен, чтобы контейнеры ходили во внешнюю сеть. Docker добавляет правила POSTROUTING сам, но вы можете усилить контроль, ограничив, что именно им разрешено с хоста.
iptables: пример egress-фильтра
# Разрешаем исходящие из контейнеров только веб и DNS
iptables -A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -A DOCKER-USER -i br+ -p udp --dport 53 -j ACCEPT
iptables -A DOCKER-USER -i br+ -p tcp -m multiport --dports 80,443 -j ACCEPT
# Остальное из docker-сетей наружу — запрет
iptables -A DOCKER-USER -i br+ ! -o br+ -j DROP
iptables -A DOCKER-USER -j RETURN
Такой набор оставляет межконтейнерный обмен в пределах одной сети на усмотрение правил Docker, но режет наружу всё, кроме явно нужного.
nftables: egress-фильтр
table inet fastfox_egress {
chain forward {
type filter hook forward priority -200;
ct state established,related accept
# Разрешаем DNS из контейнеров
iifname "br+" udp dport 53 accept
# Разрешаем только веб исход
iifname "br+" tcp dport { 80, 443 } accept
# Межмостовая изоляция
iifname "br+" oifname "br+" drop
# Остальное из docker-сетей наружу — запрет
iifname "br+" drop
counter accept
}
}
Если применяете строгий egress-контроль, тестируйте обновление пакетов в контейнерах, резолвинг, время синхронизации и прочие служебные запросы — часто забывают про NTP, реестры образов, зеркала пакетов.

Публикация портов: DNAT и защита
Публикация портов -p 0.0.0.0:443:443 создаёт правила DNAT и соответствующие ACCEPT в FORWARD. С точки зрения безопасности надёжнее держать минимальный периметр: выносить наружу только фронтенд-прокси, а внутренние сервисы оставлять в приватных сетях. Для опубликованных портов можно добавить базовую stateful-защиту и ограничение по источникам.
iptables: ограничение источников для опубликованного порта
# Разрешаем 443 только из выбранной подсети (пример)
iptables -I DOCKER-USER 1 -p tcp -s 203.0.113.0/24 --dport 443 -j ACCEPT
iptables -A DOCKER-USER -p tcp --dport 443 -j DROP
iptables -A DOCKER-USER -j RETURN
Поскольку DOCKER-USER идёт раньше DOCKER, здесь удобно отрезать нежелательные источники до того, как трафик попадёт в докеровские правила.
nftables: ограничение источников
table inet perimeter {
chain input {
type filter hook input priority -200;
ct state established,related accept
tcp dport 443 ip saddr 203.0.113.0/24 accept
tcp dport 443 drop
counter accept
}
}
Для DNAT на контейнеры правила сработают на forward, но логика та же: сначала отсечь ненужные источники, потом пропустить разрешённые к докеровским цепям.
Sysctl и ядро: что важно включить
Чтобы фильтрация на мостах работала, нужен модуль br_netfilter и соответствующие sysctl. Также включите маршрутизацию, если у вас есть форвардинг трафика.
# Разово в рантайме
modprobe br_netfilter
sysctl -w net.bridge.bridge-nf-call-iptables=1
sysctl -w net.bridge.bridge-nf-call-ip6tables=1
sysctl -w net.ipv4.ip_forward=1
# Постоянно — файл /etc/sysctl.d/99-docker.conf
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
Параметры bridge-nf-call-* направляют трафик, проходящий через Linux bridge, в стек iptables/nftables. Без них часть пакетов может обходить ваш файрвол.
UFW и firewalld: как не поссорить их с Docker
UFW и firewalld абстрагируют сложность, но Docker меняет правила напрямую. Распространённые эффекты: опубликованные порты не работают при FORWARD=DROP, или наоборот — UFW считает всё закрытым, а контейнеры доступны.
- UFW: по умолчанию смена
DEFAULT_FORWARD_POLICYнаACCEPTпомогает не ломать Docker, но ослабляет общий периметр. Более контролируемый путь — оставитьFORWARD DROP, а ограничения вносить черезDOCKER-USER, как в примерах выше. - firewalld: Docker может помещать интерфейсы мостов в зону
trusted. Проверьте зоны и сервисы, при необходимости зафиксируйте зону дляbr-*и добавьте периметровые правила на уровне nft.
Главный принцип: не пытайтесь «переписать» цепочки
DOCKER. ИспользуйтеDOCKER-USERи собственные ранние nft-цепочки. Так вы не сломаете автоматику Docker и сохраните свой контроль.
Для общего каркаса и типовых пресетов под nft см. подробный разбор в материале Гид по nftables для VDS. Для периметра SSH и базовой защиты хоста обратите внимание на Безопасность VDS: SSH и файрвол.
Диагностика: что смотреть, когда «ничего не работает»
- Маршрутизация и интерфейсы:
ip addr,ip route, проверьтеbr-*,veth,docker0. - Правила:
iptables -S,iptables -t nat -S,nft list ruleset. - Conntrack:
conntrack -Lилиss -ntpдля состояний соединений. - Трассировка nft:
nft monitor traceпокажет, какое правило сработало. - Пакеты:
tcpdump -i br-XXXXи на соответствующемveth.
Переход с iptables на nftables (или наоборот): как не обжечься
Современные дистрибутивы по умолчанию используют iptables-совместимость поверх nftables (iptables-nft). Docker с этим дружит. Определитесь, каким инструментом вы управляете вручную: если пишете nft-правила, держите их в отдельных таблицах и задавайте приоритет, чтобы они срабатывали до цепочек, созданных через совместимость. Если остаетесь на «чистом» iptables, не смешивайте подходы, чтобы не усложнять порядок обработки.
Типичные шаги миграции:
- Зафиксировать существующий ruleset:
iptables-saveиnft list ruleset. - Определить, где Docker создаёт свои цепочки (в
ipсемействе фильтра/НАТ). - Перенести свой каркас в
nftablesс нужным приоритетом или оставить его вiptables, но не переписывать докеровские цепи. - Проверить
sysctlдля bridge и форвардинга.
Пример: «прод» и «стейджинг» на одном хосте
Задача: два проекта в отдельных сетях, прод публикует 443, стейджинг доступен только с админской подсети, между сетями — ноль связности, исход наружу — только 80/443 и DNS.
- Создаём две user-defined сети с непересекающимися подсетями (compose или
docker network create). - Включаем sysctl для bridge и forwarding.
- Применяем каркасные правила из разделов выше.
- Добавляем в
DOCKER-USERallowlist для 443 с нужного источника (если требуется ограничение) и общий egress-фильтр. - Проверяем tcpdump на
br-prodиbr-stage, убеждаемся, что межсетевой трафик дропается.
Частые ошибки и анти-паттерны
- Ставить
ACCEPTповсюду «чтобы заработало». Так вы теряете контроль. Оставьте stateful-правила и DOCKER-USER для точечных разрешений. - Менять/удалять цепочки
DOCKER. Docker их восстановит, а вы получите гонки состояний. Работайте через DOCKER-USER и собственные nft-цепи. - Игнорировать
br_netfilterиbridge-nf-call-*. Тогда фильтрация на мостах частично не сработает. - Смешивать UFW/firewalld с ручными правками без понимания порядка. Фиксируйте зоны, приоритеты и используйте трассировку nft.
- Публиковать внутренние сервисы наружу. Лучше заведите обратный прокси и приватные сети.
Итог: рецепт жёсткого, но не хрупкого контура
Держите минимальные политики на INPUT и FORWARD, не ломая автоматику Docker. Все периметровые и межмостовые ограничения — в DOCKER-USER (для iptables) или в ранних цепочках nftables с отрицательным приоритетом. Разделяйте контейнеры по user-defined сетям, отключите дефолтный мост, включите фильтрацию моста в ядре, контролируйте egress. Отладку ведите через nft monitor trace, conntrack и tcpdump. При таком подходе вы сохраняете стабильность Docker-сетей и получаете предсказуемый, прозрачный контур безопасности.


