Docker динамически прописывает правила в iptables после своей загрузки. Делает он это хитрозакрученным способом. При этом порт докера становится доступен из интернета вне зависимости от того, какие правила были раньше прописаны в iptables. Расскажу как закрыть порт.
Первая уловка в том, что правила для DOCKER пишутся не в INPUT/OUTPUT у iptables, а в FORWARD. Иначе они просто не будут работать. Вторая уловка в том, что Docker создаёт себе цепочки, в которые и пишет правила. Эти цепочки обрабатываются в какой-то невероятно закрученной последовательности.
Официальная документация рекомендует изменять только цепочку DOCKER-USER, что мы конечно же делать не будем (потому что от версии к версии она вообще может не создаваться). Для закрытия порта из интернета мы используем цепочку DOCKER. Нагло прописав следующие два правила:
iptables -I DOCKER -i внешний_интерфейс ! -s внешний_ip_сервера -j REJECT
iptables -I DOCKER -i внешний_интерфейс -m state —state ESTABLISHED,RELATED -j ACCEPT
Не забудьте изменить внешний_интерфейс на имя интерфейса, который можно найти используя команду ifconfig (он будет со значением внешний_ip_сервера в одном блоке) либо команду route (в одной строке с «default»).
Как можно заметить, правила будут сохранены в обратном порядке, потому что «-I» добавляет правило в начало цепочки. То есть после выполнения этих команд в iptables будет сначала делаться ACCEPT соединения, а только потом REJECT.
Автоматизация создания правил
Остаётся последняя задача — применять эти правила в том случае, если docker только запустился. Потому что при перезапуске он прописывает правила в iptables динамически, затирая свои существующие цепочки. К сожалению, не знаю изящного способа привязки выполнения bash команд к моменту перезапуска docker.
Единственная мысль — сделать sh файл и запускать его каждый час. А внутри файла сделать проверку на сущестование нужных правил. И если правила не прописаны, то прописывать их. Содержание этого файла будет таким:
#!/bin/bash
{ iptables -L DOCKER -v -n | grep REJECT | grep внешний_интерфейс; } || { iptables -I DOCKER -i внешний_интерфейс ! -s внешний_ip_сервера -j REJECT }
{ iptables -L DOCKER -v -n | grep ACCEPT | grep внешний_интерфейс; } || { iptables -I DOCKER -i внешний_интерфейс -m state —state ESTABLISHED,RELATED -j ACCEPT }
Если команды в первых фигурных скобках возвращают пустую строку, то выполняются команды из вторых фигурных скобок. Таким образом, если нужные правила iptables уже существуют, то они второй раз не будут установлены.
Ставим выполнение этого файла на планировщик CRON раз в час. И если докер будет перезагружен, то правила будут прописаны заново.
P.S.
Можете проверить доступность порта с помощью утилиты NetCat до и после выполнения команд: «NetCat. Как проверить заблокированные порты«
P.P.S.
Docker делает неприятные неожиданности не только на сервере. К примеру, если роутер, который выдаёт докеру ip адрес, имеет функцию UPnP, то Docker откроет внешний порт на роутере и пробросит его к себе. После чего кто угодно сможет добраться до вашей машины из интернета. Поэтому строго следите за Docker контейнерами. И ругайте их, если будут самовольничать.