Глиф Учебники

Docker открывает порты в обход ufw

Справочник · Docker · 2 минуты чтения

На Linux Docker сам добавляет правила в брандмауэр (iptables или nftables) для каждого опубликованного порта — -p 5432:5432 или ports: в Compose. Эти правила срабатывают раньше, чем правила ufw. Поэтому sudo ufw deny 5432 такой порт не закроет, а в sudo ufw status правил Docker не видно вовсе. Об этом прямо предупреждает документация Docker.

Решение: не публиковать лишнее

1. Порты, нужные только другим контейнерам, не публикуйте совсем. База данных, Redis, внутренний API — контейнеры в одной сети Docker (и все сервисы одного проекта Compose) видят друг друга без -p:

YAML
services:
  app:
    image: myapp:1.0
    ports:
      - "8000:8000"
  db:
    image: postgres:18
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}

У db нет ports, и снаружи база недоступна. Сервис app подключается к ней по адресу db:5432.

2. То, что нужно только самому серверу, публикуйте на 127.0.0.1:

Bash
docker run -d --name adminer -p 127.0.0.1:8080:8080 adminer

Порт доступен только с самого сервера. Подключиться к нему с вашего компьютера можно через туннель SSH:

Bash
ssh -L 8080:127.0.0.1:8080 ivan@192.168.1.10

Пока соединение открыто, http://localhost:8080 на вашем компьютере ведёт на порт сервера.

3. Наружу — только то, что должно быть видно всем. Обычно это один обратный прокси (Nginx, Caddy, Traefik) на портах 80 и 443, а сайты за ним публикуются на 127.0.0.1 или вовсе без -p, в общей с прокси сети.

Проверьте снаружи

Проверка с самого сервера ничего не скажет — проверяйте с другого компьютера:

Bash
nc -zv 192.168.1.10 5432

Ответ succeeded или open значит, что порт открыт для сети. Отказ в соединении или ожидание без ответа — порт закрыт. На сервере список опубликованных портов показывает docker ps (столбец PORTS): адрес 0.0.0.0 (для IPv6 — ::) перед портом значит «доступен со всех адресов».

Чего не делать

Не отключайте управление брандмауэром в Docker ("iptables": false в daemon.json), чтобы «вернуть» ufw. По документации Docker это ломает сеть контейнеров, и при этом все порты контейнеров могут стать доступны из локальной сети. Такую настройку делают только те, кто готов писать все правила сам.

Не полагайтесь на то, что ufw «и так всё закрыл». После ufw enable с запретом всего входящего порт контейнера с -p 3306:3306 по-прежнему открыт всему интернету, если сервер в нём.

Если нужны правила фильтрации

Ограничить доступ к опубликованным портам по адресам можно правилами в цепочке DOCKER-USER — её Docker проверяет раньше своих правил. Это уже ручная настройка iptables; порядок описан в документации Docker (Packet filtering and firewalls). Для большинства серверов проще и надёжнее первые три способа.

До версии Docker 28.0 порт, опубликованный на 127.0.0.1, в некоторых случаях был доступен соседям по локальной сети (компьютерам за тем же коммутатором). Держите Docker обновлённым.

Как работает сам ufw — в теме Ubuntu, справка Как открыть порт в Ubuntu.