Проблема
Docker по умолчанию управляет собственными NAT-правилами. Поэтому ситуация 'ufw deny' не всегда означает, что порт действительно закрыт снаружи: published port может пройти через Docker-цепочки раньше ожидаемого фильтра.
Это особенно опасно для security-лабораторий. DVWA, Juice Shop, тестовые API и временные admin UI должны быть доступны только runner/control plane, а не всему интернету.
Решение
Для Docker-хостов нужен отдельный exposure check: docker ps, ss, iptables/nft DOCKER-USER, внешний curl с недоверенного IP и проверка allowlist с control plane.
Базовая модель: контейнер может слушать локально, но вход извне разрешается только через reverse proxy, VPN или control plane. Для прямой публикации используется DOCKER-USER deny-by-default и точечные RETURN для доверенных адресов.
Что проверить руками
- Все published ports: 0.0.0.0:port и [::]:port.
- Есть ли DOCKER-USER правила до Docker accept rules.
- Открываются ли lab ports с внешнего адреса, который не входит в allowlist.
- Задокументирована ли причина существования каждого тестового контейнера.
Как это должно попадать в отчёт
- Открытый lab/staging порт - это не косметика, а риск компрометации тестовой среды и pivot в control plane.
- Finding должен содержать внешний proof of reachability, список контейнеров, firewall path и конкретный retest: внешний адрес получает timeout/403, control plane продолжает получать 200.
Безопасный пример: инвентаризация Docker-портов и DOCKER-USER
#!/usr/bin/env bash
set -euo pipefail
echo "[published containers]"
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Ports}}'
echo
echo "[listening ports]"
ss -lntup | awk 'NR==1 || /docker-proxy|:3000|:3001|:3002|:8080|:9090/'
echo
echo "[docker-user policy]"
iptables -S DOCKER-USER 2>/dev/null || echo "DOCKER-USER chain not found"
echo
echo "retest idea: run curl from an untrusted network and from the control plane; the result must differ by policy, not by accident."