К основному содержимому

Docker обходит UFW: как закрыть порт контейнера

Безопасность · 31.10.2026 · 5 мин чтения
Иллюстрация к статье «Docker обходит UFW: как закрыть порт контейнера»

Администратор включает UFW, запрещает все входящие соединения, а порт контейнера всё равно открыт из интернета. Это не ошибка конфигурации UFW: Docker управляет iptables сам, и трафик к опубликованным портам не доходит до правил, которые пишет UFW. Ниже — как увидеть проблему и как закрыть порты правильно.

Почему UFW не защищает опубликованный порт

UFW добавляет правила в цепочку INPUT: она обрабатывает пакеты, адресованные самому серверу. Когда вы публикуете порт ключом -p, Docker создаёт правило DNAT в таблице nat и перенаправляет пакеты контейнеру. Такой трафик идёт не на сам сервер, а транзитом через цепочку FORWARD, где первыми стоят правила Docker. Правила UFW из INPUT к нему не применяются.

ЦепочкаКто пишетЧто обрабатывает
INPUTUFWТрафик к самому серверу
DOCKER-USERАдминистраторТранзитный трафик к контейнерам, проверяется первым
DOCKERDockerРазрешения для опубликованных портов

Как проверить, что порт виден снаружи

Запустите тестовый контейнер с опубликованным портом и посмотрите на столбец PORTS: запись 0.0.0.0:8080 означает, что порт слушает на всех интерфейсах. Затем проверьте доступность с другого компьютера. Адрес в примере взят из диапазона для документации, подставьте адрес своего сервера.

docker run -d --name web -p 8080:80 nginx
docker ps
ufw status
nc -zv 203.0.113.10 8080

Если соединение прошло, а ufw status показывает запрет, вы столкнулись именно с этим поведением. Посмотреть правила Docker можно так:

iptables -L DOCKER-USER -n -v --line-numbers
iptables -t nat -L DOCKER -n
iptables -L FORWARD -n -v
ss -tlnp

Способ первый: публикация только на 127.0.0.1

Самый простой и надёжный путь — не открывать порт во внешнюю сеть, если он нужен только локально, например обратному прокси-серверу на этом же хосте. Укажите адрес привязки перед портом. Тогда внешний доступ к порту невозможен, а Nginx на хосте по-прежнему обращается к контейнеру через 127.0.0.1.

docker run -d --name web -p 127.0.0.1:8080:80 nginx

services:
  web:
    image: nginx
    ports:
      - "127.0.0.1:8080:80"

Адрес привязки по умолчанию можно задать и для всех контейнеров параметром ip в файле /etc/docker/daemon.json, после чего потребуется перезапуск Docker. Параметр iptables со значением false отключает управление правилами, но тогда ломается выход контейнеров в сеть, поэтому как постоянное решение его не рекомендуют.

Способ второй: правила в DOCKER-USER

Если порт должен быть доступен только с определённых адресов, добавляйте правила в цепочку DOCKER-USER: Docker не затирает её и проверяет раньше собственных правил. К моменту проверки пакет уже прошёл DNAT, поэтому порт назначения изменился. Чтобы отфильтровать по исходному порту, используйте модуль conntrack с параметром --ctorigdstport.

iptables -I DOCKER-USER -i eth0 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j DROP
iptables -I DOCKER-USER -i eth0 -s 198.51.100.7 -p tcp -m conntrack --ctorigdstport 8080 --ctdir ORIGINAL -j ACCEPT
iptables -L DOCKER-USER -n -v --line-numbers

Правило ACCEPT вставлено позже DROP, поэтому оказалось выше и сработает первым. Эти правила не переживают перезагрузку сами по себе: сохраните их, например пакетом iptables-persistent, и проверьте результат после перезапуска Docker и сервера. Подробнее о цепочках — в статье iptables: настройка файрвола Linux с нуля.

Способ третий: изолированные сети Docker

Контейнерам, которым не нужен внешний доступ, порты публиковать не требуется. Базы данных и кэши достаточно подключить к общей пользовательской сети: контейнеры обращаются друг к другу по имени, а снаружи они не видны. Сеть с ключом --internal дополнительно лишена выхода в интернет.

docker network create --internal backend
docker run -d --name db --network backend mariadb

В Docker Compose то же делается блоком networks и параметром internal: true, подробности — в статье Docker Compose: запуск стека приложений. Базовая настройка файрвола описана в материале UFW Firewall на VPS, а установка Docker — в статье Docker на VPS: установка и запуск контейнеров.

Частые вопросы

Почему Docker открывает порты в обход UFW?

Docker сам добавляет правила в iptables: DNAT в таблице nat и разрешения в цепочке FORWARD. Трафик к контейнеру идёт транзитом, а не в цепочку INPUT, где работает UFW. Поэтому запрет в UFW для опубликованного порта не срабатывает, и нужны правила DOCKER-USER или привязка к 127.0.0.1.

Достаточно ли публикации на 127.0.0.1?

Если приложение нужно только процессам на этом же сервере, например Nginx в роли обратного прокси, привязки к 127.0.0.1 достаточно: порт не слушает внешние интерфейсы. Если доступ нужен избранным внешним адресам, используйте правила DOCKER-USER или закрытую сеть, например VPN.

Нужно ли публиковать порты для связи контейнеров между собой?

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

Сохранятся ли правила DOCKER-USER после перезагрузки?

Сами по себе нет: правила iptables хранятся в памяти ядра. Сохраните их штатным способом дистрибутива, например пакетом iptables-persistent, и после перезагрузки проверьте командой iptables -L DOCKER-USER, что правила на месте и порядок не нарушен запуском Docker.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке