Segurança de contêineres e imagens Docker de ponta a ponta
Proteja todo o ciclo de vida do contêiner: imagens base, builds, assinatura, varredura, controles de registro e execução, com sinais de detecção e uma lista.
Os contêineres prometem uma unidade de software limpa e reproduzível, mas essa promessa só se sustenta se a imagem, o pipeline de build, o registro e o tempo de execução forem protegidos em conjunto. Uma vulnerabilidade introduzida numa imagem base, um segredo assado numa camada ou um passo de build comprometido pode viajar sem alteração para todo ambiente que baixa o artefato. Este artigo percorre o ciclo de vida do contêiner de ponta a ponta pela ótica do defensor, no enquadramento entender para defender. Observamos onde o risco entra em cada etapa, qual telemetria permite flagrar o abuso e quais controles tornam uma imagem de contêiner confiável, do notebook do desenvolvedor ao nó de produção.
A cadeia de suprimentos de contêineres como superfície de ataque
Ajuda pensar uma imagem de contêiner não como um arquivo, mas como uma cadeia de custódia. O código-fonte vira um build, o build baixa dependências e uma imagem base, o resultado é empurrado para um registro, e um escalonador acaba por executá-lo em algum lugar. Cada elo é um ponto onde um atacante ou um acidente pode injetar algo não previsto. A meta defensiva é a procedência: em qualquer ponto você deveria conseguir responder de onde veio uma imagem, o que a compôs, quem a construiu e se foi alterada desde então. Quando falta procedência, uma única camada envenenada pode se propagar em silêncio por toda a sua frota.
Por isso o pensamento de cadeia de suprimentos ocupa hoje o centro da segurança de contêineres. Não basta escanear a imagem final; é preciso proteger o processo que a produz e poder provar que o que roda em produção é exatamente o que seu pipeline construiu.
Escolher e manter imagens base
A maior parte da superfície de ataque de uma imagem vem do que você herdou, não do que escreveu. Uma imagem base cheia de shells, gerenciadores de pacotes e utilitários de sistema entrega a um invasor uma caixa de ferramentas no momento em que ele aterrissa. Prefira bases mínimas ou distroless que contenham apenas sua aplicação e suas dependências de execução, e fixe-as a um digest imutável em vez de uma tag mutável como latest, para que um rebuild não baixe em silêncio uma imagem diferente e possivelmente adulterada. Reconstrua regularmente para absorver correções de segurança a montante; um digest fixo que nunca atualiza é seguro contra surpresa, mas acumula lentamente vulnerabilidades conhecidas.
Rastreie a procedência de suas imagens base. Favoreça editores oficiais ou verificados e trate uma fonte sem manutenção como um passivo. Quanto menos pacotes uma imagem contém, menor a superfície que você deve corrigir e mais silencioso seu scanner de vulnerabilidades, o que por sua vez torna os achados reais mais visíveis.
Construir imagens com segurança
O build é onde os segredos vazam com mais frequência. Uma credencial passada como argumento de build ou copiada numa etapa e apagada numa posterior ainda vive no histórico da imagem, recuperável por qualquer um que a baixe. Use montagens de segredo em tempo de build que nunca persistem numa camada e mantenha os segredos totalmente fora do Dockerfile. Builds multiestágio permitem compilar ou instalar numa etapa construtora gorda e copiar apenas o artefato pronto para uma etapa final enxuta, deixando para trás compiladores, caches de pacotes e credenciais intermediárias.
Execute o contêiner como usuário não root declarando um usuário dedicado na imagem, para que mesmo um tempo de execução que não sobrescreva o usuário ainda não rode como root. Escolha um layout de sistema de arquivos amigável a somente leitura, evite ADD com URLs remotas e prefira COPY com fontes explícitas. Gere uma lista de materiais de software durante o build para ter um inventário de cada componente, que se torna inestimável no dia em que uma nova vulnerabilidade é divulgada e você precisa saber na hora se está afetado.
Assinar e verificar a procedência
Uma assinatura transforma a procedência de uma afirmação em algo que você pode impor. Assine as imagens no fim do build com uma ferramenta como o cosign do Sigstore e registre atestações que descrevam como a imagem foi construída e o que a compôs. Na implantação, um controlador de admissão ou um motor de políticas verifica essa assinatura e se recusa a executar qualquer coisa sem assinatura ou assinada com uma chave inesperada. Isso fecha a lacuna entre o registro e o tempo de execução: mesmo que um atacante empurre uma imagem maliciosa para o seu registro, ela não pode rodar sem uma assinatura válida do seu pipeline.
A assinatura sem chave atada à sua identidade de CI remove o fardo de gerenciar chaves de assinatura de vida longa e liga cada assinatura a um fluxo de trabalho verificável. A propriedade defensiva chave é que a confiança flui do seu sistema de build, não do mero fato de uma imagem residir no seu registro.
Escanear e barrar
A varredura de vulnerabilidades pertence a vários pontos: no pull request para que os desenvolvedores vejam problemas cedo, no pipeline como um portão que pode falhar o build, e continuamente contra imagens já no registro, porque novas vulnerabilidades são divulgadas contra imagens que não mudaram. Configure o portão com uma política adequada ao seu risco — por exemplo, barrar em problemas críticos e altos corrigíveis enquanto rastreia o resto — e dê às equipes um caminho claro de exceção com prazo de validade, para que os portões não sejam simplesmente desativados sob pressão de prazo. Escaneie também por segredos embutidos e má configuração, não só vulnerabilidades conhecidas de pacotes.
Lembre-se de que um scanner reporta o que sabe hoje. Combine-o com a lista de materiais de software para que, quando surgir uma vulnerabilidade totalmente nova, você possa consultar seu inventário em vez de reescanear o mundo. A combinação de portão e inventário é o que permite responder em minutos em vez de dias.
Proteção em execução e higiene do registro
Uma vez que um contêiner roda, os controles deslocam-se para restringi-lo e observá-lo. Descarte capacidades do Linux, aplique um perfil seccomp, execute com um sistema de arquivos raiz somente leitura e nunca conceda a flag privileged nem monte o socket do Docker do host num contêiner, porque qualquer um dos dois entrega efetivamente o nó. Do lado do registro, exija autenticação, restrinja com firmeza as permissões de pull e push, habilite tags imutáveis para que uma imagem publicada não seja trocada sob os consumidores, e pode imagens não confiáveis ou obsoletas. Um registro para o qual qualquer um pode empurrar é um canal de distribuição para o que um atacante quiser executar.
Isole os executores de build das credenciais de produção. Um executor de CI comprometido com acesso amplo é um dos pontos de apoio mais danosos que um atacante pode obter, porque pode reescrever as próprias imagens em que você confia. Dê aos executores mínimo privilégio, identidades efêmeras e nenhum acesso permanente a produção.
Detecção: sinais ao longo do ciclo de vida
A detecção abrange toda a cadeia. No pipeline, vigie builds que baixam de fontes inesperadas, falhas de verificação de assinatura e picos súbitos em achados do scanner. No registro, alerte para pushes de identidades incomuns, pulls de imagens que nunca foram promovidas e mutações de tag onde você esperava imutabilidade. Em execução, um sensor de comportamento como o Falco ou um EDR ciente de contêineres sinaliza um shell aberto dentro de um contêiner, um processo escrevendo num caminho somente leitura, uma conexão de saída inesperada ou uma tentativa de alcançar o socket do runtime de contêineres. Correlacionar um push ao registro com um comportamento anômalo em execução muitas vezes revela uma imagem envenenada antes de ela se espalhar.
Retenha esses registros por mais tempo do que o tempo típico de permanência de um atacante e envie-os para fora dos hosts que os geram. Dados de procedência, resultados de verificação de assinatura e alertas de execução juntos permitem responder à pergunta de todo incidente: o que rodou, de onde veio e ainda está rodando em algum outro lugar?
Uma lista de verificação prática
Base: mínima ou distroless, fixada a um digest, de um editor verificado, reconstruída regularmente. Build: usuário não root, multiestágio, montagens de segredo em vez de argumentos, lista de materiais de software gerada, sem socket do host. Procedência: imagens assinadas, atestações registradas, a admissão verifica assinaturas. Varredura: no PR, no pipeline como portão, contínua no registro, varredura de segredos e má configuração incluída, exceções com prazo. Registro: autenticado, push de mínimo privilégio, tags imutáveis, imagens obsoletas podadas. Execução: capacidades descartadas, seccomp, sistema de arquivos somente leitura, sem privileged, sem montagem do socket do Docker. Detecção: alertas de pipeline, registro e execução correlacionados, registros retidos além do tempo de permanência.
FAQ: A varredura torna minhas imagens seguras?
A varredura é necessária, mas não suficiente. Ela informa sobre vulnerabilidades conhecidas em componentes conhecidos, o que é valioso, mas nada diz sobre um passo de build malicioso, um segredo vazado que ela não reconhece, um tempo de execução com excesso de privilégios ou uma vulnerabilidade totalmente nova divulgada amanhã. Trate a varredura como uma camada ao lado de procedência, assinatura, imagens mínimas, confinamento em execução e detecção. A segurança vem da combinação, não de um único portão.
FAQ: Imagens distroless ou mínimas são sempre melhores?
Elas reduzem drasticamente a superfície de ataque e o ruído do scanner, e para a maioria dos serviços de produção são a escolha certa por padrão. A contrapartida é a depurabilidade, já que não há shell nem gerenciador de pacotes para bisbilhotar quando algo quebra. A resposta madura é manter as imagens de produção mínimas e usar contêineres de depuração efêmeros ou uma imagem separada mais rica para diagnóstico, de modo a ter uma superfície pequena em produção sem perder a capacidade de investigar.
Conclusão
A segurança de contêineres não é um controle único, mas uma cadeia que só é tão forte quanto seu elo mais fraco. Parta de uma base mínima, fixada e verificada; construa sem vazar segredos e como usuário não root; assine e ateste para que a procedência seja imponível; escaneie e barre em cada etapa; tranque o registro e o tempo de execução; e instrumente todo o ciclo de vida para que o abuso seja visível. Quando a procedência flui sem quebra da fonte ao contêiner em execução, uma camada envenenada não tem onde se esconder, e a reprodutibilidade que torna os contêineres atraentes também os torna defensáveis.
