До основного вмісту

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.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці