Комментарии 11
Очень классный разбор про userland-proxy. Я всегда думал, что если у Docker выключен userland-proxy, то порты все равно будут слушаться, но не было времени разобраться. Получается, что если я отключу userland-proxy в daemon.json, то listeners.py перестанет видеть опубликованные порты (потому что их будет слушать не docker-proxy, а NAT), и мне придется смотреть docker ps? А как вы на практике это решаете — парсите docker ps или всё-таки смотрите правила iptables?
Да, всё так. С "userland-proxy": false никакого docker-proxy нет, порт обслуживает только DNAT-правило в iptables, и в /proc/net/tcp хоста он не появится. Поэтому listeners.py в таком режиме опубликованных портов не увидит, он полезен скорее как проверка того, что слушают сами сервисы на хосте.
На практике я бы брал источником правды сам Docker: docker ps --format '{{.Names}} {{.Ports}}' или docker inspect (поле NetworkSettings.Ports), там видно, на какой адрес хоста опубликован порт. Правила iptables (iptables -t nat -S DOCKER) — хорошая перепроверка, но парсить их как основной источник неудобно. И третий слой, который ничем не заменить: скан снаружи с другой машины, например nmap по внешнему IP. Только он показывает, что реально доступно из интернета.
Писал уже в той теме. У iptables есть таблица raw, она обрабатывается до ната и вообще до коннтрэка. Это практически первое куда попадает сетевой трафик из буфера карты. Весь заведомый мусор имеет смысл фильтровать там. Во первых туда мало умников залезают вроде докера (я пока не встречал). Во вторых это уменьшает количество мусора в контрэке и освобождает немного (а при флуде на хост - очень даже много) ресурсов. Имеет смысл первым правилом сделать удалённое управление с конкретного адреса и никогда его не трогать, чтобы не потерять связь с сервером. в случае ддоса вторым правилом можно будет добавить глобальный дроп, это вернёт вам связь с хостом если не перегружен аплинк. Но надо быть аккуратным, учитывать сдвиг в цепочке правил и т.п.
Вашего решения это не отменяет, должно быть несколько уровней защиты. да и пароли с обновлениями на всех базах должны стоять даже если они доступны только через локалсокет и т.п.
О, спасибо за разбор! Честно, я как-то не подумал про userland-proxy. Получается, если его отключить ради производительности, то мой скрипт на /proc просто ослепнет.
Про docker ps и docker inspect — согласен, это надежнее. А вот про nmap снаружи — это прям золотые слова. Никакой локальный скрипт не покажет, что реально торчит в интернет, если на роутере или в облаке есть свои фаерволы. Буду знать, спасибо!
Прастити извинити.
Я правильно понимаю что при использовании ufw, postgres и при отсутствии docker - проблемы нет?
Интересно. А какой CRI тогда используете? Podman? А там точно нет такой проблемы с UFW? Я бы всё же проверил.
Ага. А вот еще одна хорошая практика - отсутствие публичных ip на хосте с постгрей, вынос сервисов на отдельну вм и их связь по локалке.
Спасибо, интересное исследование. Ещё могу порекомендовать ознакомиться с проектом https://github.com/chaifeng/ufw-docker, там тоже несколько полезностей есть.

UFW говорит «всё закрыто», а Postgres торчит наружу: пишем аудит портов для docker compose на Python