Aikido

Um checklist de segurança Docker direto ao ponto para o desenvolvedor atento a vulnerabilidades

Escrito por
Mackenzie Jackson

Por que você está aqui?

Você quer saber a resposta real para duas perguntas sobre segurança Docker:

O Docker é seguro para uso em produção?

Sim e não. O Docker usa um modelo de segurança que se baseia em namespaces e isolamento de recursos, tornando os processos internos mais seguros contra ataques específicos do que executar suas aplicações diretamente de uma VM Cloud ou sistema bare metal. Apesar dessa camada, ainda existem muitas maneiras para os invasores acessarem seu Container, permitindo-lhes ler informações confidenciais, executar ataques de negação de serviço (DoS) ou até mesmo obter acesso root ao sistema host.

Como posso melhorar a segurança do meu Docker (de uma forma não tão dolorosa)?

Vamos guiá-lo pelas vulnerabilidades Docker mais comuns e severas, pulando as recomendações básicas que você encontrará em todo o Google, como usar imagens oficiais e manter seu host atualizado. Em vez disso, vamos levá-lo diretamente a novas opções Docker e linhas de Dockerfile que tornarão sua nova implantação padrão de Container Docker muito mais segura do que nunca.

O checklist de segurança Docker sem enrolação

Torne os sistemas de arquivos dentro do Container somente leitura

O que você ganha?

Você impede que um invasor edite o ambiente de tempo de execução do seu Container Docker, o que poderia permitir-lhes coletar informações úteis sobre sua infraestrutura, coletar dados de usuários ou conduzir um ataque de DOS ou ransomware diretamente.

Como você o configura?

Você tem duas opções, seja em tempo de execução ou dentro da sua configuração Docker Compose.

Em tempo de execução: docker run --read-only your-app:v1.0.1

No seu arquivo Docker Compose:

services:
webapp:
image: your-app:v1.0.1read_only: true
...

Bloquear escalonamento de privilégios

O que você ganha?

Você impede que seu Container Docker — ou um invasor que esteja mexendo dentro do referido Container — de habilitar novos privilégios, mesmo em nível de root, com setuid ou setgid. Com acesso mais permissivo ao seu Container, um invasor poderia acessar credenciais na forma de senhas ou chaves para partes conectadas da sua implantação, como um banco de dados.

Como você o configura?

Mais uma vez, em tempo de execução ou dentro da sua configuração Docker Compose.

Em tempo de execução: docker run --security-opt=no-new-privileges your-app:v1.0.1

No seu arquivo Docker Compose:

services:  
	webapp:    
		image: your-app:v1.0.1    
		security_opt:      
			- no-new-privileges:true    
		...

Isole suas redes Container-para-Container

O que você ganha?

Por padrão, o Docker permite que todos os Containers se comuniquem via rede docker0, o que pode permitir que um invasor se mova lateralmente de um Container comprometido para outro. Se você tiver serviços discretos A e B em Containers Y e Z, e eles não precisam se comunicar diretamente, isolar suas redes proporciona a mesma experiência ao usuário final, ao mesmo tempo em que previne o movimento lateral para uma melhor segurança do Docker.

Como você o configura?

Você pode especificar redes Docker em tempo de execução ou dentro da sua configuração do Docker Compose. No entanto, primeiro você precisa criar a rede:

docker network create your-isolated-network

Em tempo de execução, adicione a --network opçãoo: docker run --network your-isolated-network your-app:v1.0.1

Ou a opção equivalente no seu arquivo Docker Compose:

services:  
	webapp:    
    	image: your-app:v1.0.1    
        networks:      
        	- your-isolated-network    
		...

Defina um usuário não-root adequado

O que você ganha?

O usuário padrão dentro de um Container é root, com um uid de 0. Ao especificar um usuário distinto, você impede que um atacante escale seus privilégios para outro usuário que possa agir sem restrições, como o root, o que anularia quaisquer outras medidas de segurança do Docker que você se esforçou para implementar.

Como você o configura?

Crie seu usuário durante o processo de build ou em tempo de execução. Em tempo de execução, você pode criar o usuário pela primeira vez ou substituir o USER que você já definiu no build.

Durante o processo de build, no seu Dockerfile:

...
RUN groupadd -r your-user
RUN useradd -r -g your-user your-user
USER myuser
...

Em tempo de execução: docker run -u your-user your-app:v1.0.1

Remova capacidades do kernel Linux

O que você ganha?

Por padrão, os Containers Docker podem usar um conjunto restrito de capacidades do kernel Linux. Você pode pensar que a equipe do Docker criou esse conjunto restrito para ser completamente seguro, mas muitas capacidades existem para compatibilidade e simplicidade. Por exemplo, Containers padrão podem alterar arbitrariamente a propriedade de arquivos, mudar seu diretório raiz, manipular UIDs de processos e ler sockets. Ao remover algumas ou todas essas capacidades, você minimiza o número de vetores de ataque.

Como você o configura?

Você pode remover capacidades e definir novas em tempo de execução. Por exemplo, você poderia remover todas as capacidades do kernel e permitir que seu Container tivesse apenas a capacidade de alterar a propriedade de arquivos existentes.

docker run --cap-drop ALL --cap-add CHOWN your-app:v1.0.1

Ou para Docker Compose:

services:
	webapp:
    	image: your-app:v1.0.1
        cap_drop:
        	- ALL
        cap_add:
        	- CHOWN
        ...

Previna fork bombs

O que você ganha?

Fork bombs são um tipo de ataque DoS que replica infinitamente um processo existente. Primeiro, eles reduzem o desempenho e restringem recursos, o que inevitavelmente aumenta os custos e pode, em última instância, travar seus contêineres ou o sistema host. Uma vez que um fork bomb é iniciado, não há como pará-lo a não ser reiniciando o Container ou o host.

Como você o configura?

Em tempo de execução, você pode limitar o número de processos (PIDs) que seu Container pode criar.

docker run --pids-limit 99 your-app:v1.0.1

Ou com Docker Compose:

services:
	webapp:
		image: your-app:v1.0.1
		deploy
			limits:
				pids: 99

Melhore a segurança do Docker monitorando suas dependências de código aberto

O que você ganha?

Os aplicativos que você containerizou para implantação com Docker provavelmente possuem uma vasta árvore de dependências.

Como você o configura?

A maneira mais 'sem enrolação' é com a análise de dependências de código aberto da Aikido. Nosso monitoramento contínuo analisa projetos escritos em mais de uma dúzia de linguagens com base na presença de lockfiles em sua aplicação e entrega uma visão instantânea de vulnerabilidades e malware. Com triagem automática que filtra falsos positivos, a Aikido oferece conselhos de remediação com os quais você pode começar a trabalhar imediatamente... não apenas depois de ler uma dúzia de outros documentos de referência e issues do GitHub.

Na Aikido, amamos projetos open-source estabelecidos como Trivy, Syft e Grype. Também sabemos por experiência que usá-los isoladamente não é uma experiência de desenvolvedor particularmente boa. Por baixo do capô, a Aikido aprimora esses projetos com regras personalizadas para preencher lacunas e revelar falhas de segurança que você não conseguiria encontrar de outra forma. Ao contrário de encadear várias ferramentas open-source juntas, a Aikido o liberta de ter que construir um script de análise ou criar um job personalizado em seu CI/CD.

Análise de dependências de código aberto na Aikido para uma segurança Docker mais completa.
Digite a legenda (opcional)

Use apenas imagens confiáveis para a segurança do Docker

O que você ganha?

Docker Content Trust (DCT) é um sistema para assinatura e validação do conteúdo e integridade das imagens oficiais que você puxa de registries Docker como o Docker Hub. Puxar apenas imagens assinadas pelo autor oferece mais garantia de que elas não foram adulteradas para criar vulnerabilidades em sua implantação.

Como você o configura?

A maneira mais fácil é definir a variável de ambiente em seu shell, o que impede você ou qualquer outra pessoa de trabalhar com imagens não confiáveis.

export DOCKER_CONTENT_TRUST=1
docker run ...

Ou, você pode definir a variável de ambiente toda vez que executar o Docker:

DOCKER_CONTENT_TRUST=1 docker run …

Atualize runtimes em fim de vida (EOL)

O que você ganha?

Uma recomendação comum para segurança de contêineres Docker é fixar imagens e dependências a uma versão específica em vez de mais recente. Em teoria, isso impede que você use novas imagens sem saber, mesmo aquelas que foram adulteradas, que introduzem novas vulnerabilidades.

Como você o configura?

Você tem alguns projetos de código aberto disponíveis para ajudar a descobrir EOLs e se preparar melhor. O projeto endoflife.date (repositório GitHub) rastreia mais de 300 produtos agregando dados de múltiplas fontes e os disponibilizando via uma API pública. Você tem algumas opções com endoflife.date e projetos similares:

  • Verifique manualmente o projeto para atualizações de dependências das quais seus aplicativos dependem e crie tickets ou issues para as atualizações necessárias.
  • Escreva um script (Bash, Python, etc.) para obter as datas de EOL das dependências da API e execute-o regularmente, como um cron job.
  • Incorpore a API pública, ou esse script personalizado, em sua plataforma de CI para falhar builds que utilizam um projeto que está se aproximando ou que já atingiu o EOL.

Como desenvolvedor, entendemos que seu tempo é valioso e muitas vezes limitado. É aqui que a Aikido pode proporcionar uma sensação de segurança — nosso recurso de análise de EOL rastreia seu código e contêineres, priorizando runtimes com o maior impacto e exposição, como Node.js ou um servidor web Nginx. Como de costume, não apenas automatizamos a coleta de informações, mas entregamos alertas com severidade apropriada para informar, não sobrecarregar você.

Um exemplo de como melhorar a segurança do Docker por meio da varredura de dependências em fim de vida útil.
Um

Limitar o uso de recursos do Container

O que você ganha?

Por padrão, contêineres não têm restrições de recursos e usarão tanta memória ou CPU quanto o agendador do host. Limitar o uso de recursos de um Container específico pode minimizar o impacto de um ataque DoS. Em vez de travar seu Container ou sistema host devido a uma exceção de falta de memória (Out of Memory Exception), o ataque DoS em andamento 'apenas' impactará negativamente a experiência do usuário final.

Como você o configura?

Em tempo de execução, você pode usar o --memory e --cpus opção para definir limites de uso de memória e CPU, respectivamente. A opção de memória aceita números com 'g' para gigabytes e 'm' para megabytes, enquanto a opção de CPU reflete o limite de CPUs dedicadas disponíveis para o Container e seus processos.

docker run --memory="1g" --cpus="2" your-app:v1.0.1

Isso também funciona com Docker Compose:

services:
	webapp:
    	image: your-app:v1.0.1
        deploy:
            limits:
            	cpus: '2'
                memory: 1G
         ...

Seu comando final e opções de Compose para segurança do Docker

A esta altura, você já viu várias dicas de segurança do Docker e as opções de CLI ou configurações relevantes para acompanhá-las, o que significa que você está bastante animado para implementá-las ou sobrecarregado com a forma de juntar tudo. Abaixo, reunimos todas as recomendações em um único comando ou template de configuração, o que o ajudará a começar a implantar Container Docker mais seguros imediatamente.

Obviamente, você vai querer alterar algumas das opções — como o nome de usuário não-root, as capacidades do kernel, os limites de recursos — com base nas necessidades da sua aplicação.

export DOCKER_CONTENT_TRUST=1
docker run \  
    --read-only \  
    --security-opt=no-new-privileges \  
    --network your-isolated-network \  
    --cap-drop ALL 
    --cap-add CHOWN \  
    --pids-limit 99 \  
    --memory="1g" --cpus="2" \  
    --user=your-user \  
    ...                 # OTHER OPTIONS GO HERE  
    your-app:v1.0.1

Você pode até querer criar um alias `drun` com o shell do seu host que você pode invocar sem ter que se lembrar de todos esses detalhes.

function drun {  
docker run \   
    	--read-only \    
        --security-opt=no-new-privileges \    
        --network your-isolated-network \    
        --cap-drop ALL 
        --cap-add CHOWN \    
        --pids-limit 99 \    
        --memory="1g" --cpus="2" \    
        --user=your-user \    
        $1 \    
        $2
}

Em seguida, execute seu alias assim, com suas opções e nome da imagem: `drun -it your-app:v1.0.1`

Se você é do tipo que usa Docker Compose, pode adaptar todas as mesmas opções em um novo template base de Docker Compose que você pode usar no futuro:

services:
	webapp:
    	image: your-app:v1.0.1
        read_only: true
        security_opt:
           - no-new-privileges:true
        networks:
           - your-isolated-network
        cap_drop:
           - ALL
        cap_add:
           - CHOWN
        deploy:
        	limits:
        		pids: 9
        		cpus: '2'
        		memory: 1G
        ...               # OTHER OPTIONS GO HERE

Bônus: Execute Docker com Container rootless

Ao instalar o Docker em qualquer sistema, seu daemon opera com privilégios de nível root. Mesmo que você habilite todas as opções acima e previna a escalada de privilégios dentro de um Container Docker, o restante do runtime do Container em seu sistema host ainda tem privilégios de root. Isso inevitavelmente amplia sua superfície de ataque.

A solução são os Container rootless, que um usuário sem privilégios pode criar e gerenciar. Não envolver privilégios de root significa muito menos problemas de segurança para o seu sistema host.

Gostaríamos de poder ajudá-lo a usar Container rootless com uma única opção ou comando, mas não é tão simples assim. Você pode encontrar instruções detalhadas no site Rootless Containers, incluindo um guia prático para Docker.

O que vem a seguir para a segurança do seu Docker?

Se você aprendeu algo com esta experiência, é que a segurança de contêineres é uma operação de longo prazo. Sempre há mais checklists de hardening e artigos aprofundados para ler sobre como proteger seus contêineres no Docker ou em seu primo mais antigo e frequentemente mal compreendido, Kubernetes. Você não pode almejar uma segurança de contêineres sem falhas — criar tempo em sua agenda de desenvolvimento agitada para abordar a segurança e, em seguida, fazer melhorias incrementais com base no impacto e na severidade, trará grandes benefícios ao longo do tempo.

Para ajudá-lo a maximizar esse processo contínuo e priorizar correções que melhorarão significativamente a segurança de sua aplicação, existe a Aikido. Acabamos de levantar uma rodada Série A de US$ 17 milhões para nossa plataforma de segurança para desenvolvedores 'sem enrolação', e adoraríamos ter você conosco.

Compartilhar:

https://www.aikido.dev/blog/a-no-bs-docker-security-checklist-for-the-vulnerability-minded-developer

Assine para receber notícias sobre ameaças.

Comece hoje, gratuitamente.

Comece Gratuitamente
Não é necessário cc

Fique seguro agora

Proteja seu código, Cloud e runtime em um único sistema centralizado.
Encontre e corrija vulnerabilidades rapidamente de forma automática.

Não é necessário cartão de crédito | Resultados da varredura em 32 segundos.