Docker может помешать закрыть порты

Недавно я настраивала сервер, на котором запускается Elasticsearch в докере.

Обращения к контейнеру предусмотрены только в пределах сервера по 9200 порту. Доступ к этому порту извне нужно было закрыть. И эта несложная, вроде бы, операция, сделала мой день. Вроде как закрытый через iptables порт оставался открытым во что бы то ни стало. В результате довольно продолжительного гугления я нашла статью другого человека, столкнувшегося с точно такой же проблемой.

https://www.jeffgeerling.com/blog/2020/be-careful-docker-might-be-exposing-ports-world

Перевод этой статьи и предлагаю вашему вниманию.


На днях я заметил странные записи в логах одного из моих веб сервисов, как будто кто-то пытается обратиться к эндпоинту моего приложения. Я удивился, так как все эндпоинты, открытые во внешний интернет, закрыты либо паролем, либо по IP. Так я думал.

За последний год я переписал этот сервис под docker. Вместо стандартного одного сервера на процесс, теперь я запускаю группу процессов на одном сервере, каждый процесс в своем docker-контейнере, и распределяю трафик между процессами по именам поддоменов. В качестве прокси-сервера — nginx.

На сервере я сделал фаерволл на основе iptables. Запуск происходит через Docker Compose, все процессы запускаются в контейнерах, каждый на отдельном порту, типа 1234, 1235, и т.д.

Объявление порта для сервиса выглядит так:

version: '3.7'
services:
  process_1234:
    ports:
      - "127.0.0.1:1234:1234"

Nginx проксирует трафик с поддомена типа service-1234.example.com (на 443 порту) на приложение в докере, которе висит на 1234 порту, service-1235.example.com — на 1235 порт, и т.д.

Я думал, что объявление порта “127.0.0.1:1234:1234” будет открывать порт только на локальный хост, в соотвествии с документацией.

В большинстве случаев, схема работает, как ожидалось.
Вот скрипт для проверки:
https://gist.github.com/geerlingguy/3dabf0ec3befb8c49bbed0ec7cd3d44c

Но в моем случае, похоже, некоторые правила iptables конфиликруют с правилами докера, и правила iptables докера открывают порты во внешнюю сеть.

Я стал думать (и гуглить), как же так получилось, что, несмотря на то, что порт вроде бы закрыт, внешние запросы к server-ip:1234 проходят, и нашел другие свидетельства той же проблемы.

В моем случае, помогло добавить новое правило к цепочке DOCKER-USER:

iptables -I DOCKER-USER -i eth0 ! -s 127.0.0.1 -j DROP

Это правило, в соответствии с документацией, ограничивает весь трафик с интерфейса eth0, кроме исходящего от localhost. Это сработало для меня, но мне не нужно расшаривать контейнеры докера в сеть. Если вам нужно получить микс контейнеров, открытых в сеть, и других сервисов, установленных прямо на сервере, этот фикс не сработает.

После внесения изменений, можно проверить, что они применились:

$ sudo iptables -L
Chain DOCKER-USER (1 references)
target prot opt source destination
DROP all -- !localhost anywhere
RETURN all -- anywhere anywhere

Для стопроцентной уверенности, что фаерволл сработал, можно поискать открытые порты (-p- значит «сканировать все порты, 1-65535»), используя nmap:

$ sudo nmap -p- [server-ip-address]

Вот так! Мне, кстати, тоже помогло это решение. Будьте внимательны и осторожны с докером!