Не думал, что этот вопрос настолько актуален — почему перестают работать очевидные правила iptables на хостовой машине, если на сервере запущены какие-либо Docker-контейнеры?

Давай разбираться.

Если коротко, то когда ты запускаешь Docker-контейнеры, всё начинает работать через жопу по правилам маршрутизации самого Docker. И плевать он хотел, что у тебя там на хостовой машине было ранее сконфигурировано в плане безопасности.

Давай рассмотрим на примере, как с этим можно побороться.

Вводные: на сервере крутится контейнер Nginx, порты 80 и 443 смотрят в мир, то есть открыты для всех.

Задача: нужно, чтобы порты 80 и 443 были доступны только для определённых IP-адресов.

Если бахнуть на хостовой машине так:

-A INPUT -s xxx.xxx.xxx.xxx/32 -p tcp -m tcp --dport 80,443 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited

То порты 80 и 443 также будут доступны всем желающим. Вот на эти грабли почти каждый и наступает.

А решение простое: немного правим свой docker-compose.yml и добавляем networks. В моём примере я добавил сеть bashdays.

version: '3.2'

services:
 nginx:
 image: nginx:1.20.1
 container_name: nginx
 ports:
 - '80:80'
 - '443:443'
 networks:
 - bashdays

networks:
 bashdays:
 driver: bridge
 driver_opts:
 com.docker.network.enable_ipv6: "false"
 com.docker.network.bridge.name: "docker_bashdays"

Ну и теперь на хостовой машине можно уже что-то запрещать или разрешать с помощью iptables.

/sbin/iptables -F DOCKER-USER
/sbin/iptables -I DOCKER-USER -i eth0 -o docker_bashdays -j DROP
/sbin/iptables -I DOCKER-USER -i eth0 -s <EXTERNAL IP> -j RETURN
/sbin/iptables -I DOCKER-USER -i eth0 -p tcp --dport 80 -s <CLIENT IP> -j RETURN
/sbin/iptables -I DOCKER-USER -i eth0 -p tcp --dport 443 -s <CLIENT IP> -j RETURN
/sbin/iptables -I DOCKER-USER -i eth0 -o docker_bashdays -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

Где <EXTERNAL IP> — это айпишник сервера, который доступен из мира. А <CLIENT IP> — собственно, айпишник клиента, которому мы разрешим коннектиться к портам 80 и 443. Всё! Теперь у нас и Docker в шоколаде, и хостовые правила iptables работают «наверное» правильно.

Не забывай правильно подставить свой интерфейс ethX/enoX, а то всякое бывает.

Важное замечание. После перезапуска контейнеров Docker снова перетрёт правила на хостовой машине. Поэтому весь этот процесс хорошо бы автоматизировать. Я сильно не заморачивался: просто закинул правила в Bash-скрипт, повесил на cron (каждую минуту), да и всё. 🧠 Закостылил так закостылил.

Сегодня чуть позже покажу более правильный скрипт, который будет следить за перезапуском Docker, но, по сути, те же яйца, только сбоку. Рад был всех вас видеть!