Quem tem medo de vulnerabilidades de container?

Iluiz Sousa
8 min readSep 4, 2021

--

Imagine que você recebeu um relatório de vulnerabilidades da sua aplicação, e que grande parte delas, estão relacionadas ao seu container que executa num determinado cluster Kubernetes. Imagine também que existe uma política de segurança recém implementada na empresa que irá “quebrar” a pipeline que não estiver em dia com todos os requisitos exigidos nessa política. Bem, isso impacta diretamente na entrega do produto e consequentemente no negócio.

Então, como corrigir vulnerabilidades de containers?

Não existe uma fórmula mágica e nem bala de prata para essa pergunta, porém, o que existe são algumas técnicas e melhores práticas que podem ser seguidas para evitar que os nossos containers fiquem vulneráveis e consequentemente afetem a segurança do nosso cluster.

Por onde começar?

Vamos ver abaixo alguns ajustes e melhores práticas que podemos seguir:

#1 Analise o relatório de vulnerabilidades

Parece óbvio, e é. A primeira coisa que devemos fazer, é ter noção das vulnerabilidades que temos que tratar. Vejamos o seguinte relatório abaixo:

293 VULNERABILIDADES.

É realmente um número assustador e desanimador na maioria dos casos. Mas vamos ver abaixo como podemos começar a mexer nesse “vespeiro”.

#2 Procure por baselines

Existem alguns baselines específicos para construção de imagens de containers. Vale a pena conferir as melhores práticas recomendadas, isso pode ser um bom ponto de partida para que você consiga corrigir as vulnerabilidades da sua imagem.

Abaixo alguns baselines específicos de segurança:

#3 Ajuste seu Dockerfile seguindo a recomendação dos baselines

O relatório traz vulnerabilidades específicas da imagem do nosso container. Faz todo o sentido voltar o nosso olhar para o Dockerfile, visto que esse é o arquivo declarativo da imagem que “da vida” ao nosso container.

Vamos observar nosso arquivo atual:

# Dockerfile app covidtrackerFROM node:10
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4000
CMD ["node", "server.js"]

É um Dockerfile simples, porém uma série de vulnerabilidades foram apontadas para esse arquivo. Vamos ver abaixo o que podemos alterar para diminuir a nossa quantidade de vulnerabilidades.

#3 Utilize muilt-stage building

É uma prática recomendada manter as imagens mínimas. Devemos evitar incluir pacotes desnecessários, reduzindo a nossa superfície de ataque.

Quando usamos multi-stage Building criamos um container intermediário com todos os componentes necessários para compilar ou gerar os artefatos para a imagem final. Uma vez que temos esses artefatos, uma nova imagem, muito reduzida de componentes, é utilizada para executar somente o necessário do nosso container.

Isso é uma prática segura e também reduz o tamanho da imagem. Então vamos modificar o nosso Dockerfile:

# Dockerfile app covidtracker#satege1 build
FROM node:lts-alpine AS buildapp
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
#stage 2 run
FROM node:lts-alpine
EXPOSE 4000
WORKDIR /usr/src/app
COPY --from=buildapp /usr/src/app .
CMD ["node", "server.js"]

Visando otimizar ainda mais a nossa imagem, iremos utilizar uma imagem base do tipo Alpine. As imagens Alpine são bem mais leves quando comparadas as imagens base padrão e possuem muito menos componentes o que vai de encontro a nossa abordagem.

# Dockerfile app covidtracker#satege1 build
FROM node:lts-alpine AS buildapp
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
#stage 2 run
FROM node:lts-alpine
EXPOSE 4000
WORKDIR /usr/src/app
COPY --from=buildapp /usr/src/app .
CMD ["node", "server.js"]

#4 Use imagens base de fontes confiáveis

Sempre prefira utilizar imagens verificadas e de repositórios confiáveis ao invés de imagens criadas por usuários desconhecidos, por mais performáticas que elas pareçam ser. Na dúvida faça você mesmo a sua imagem.

#5 Crie um usuário para o seu container

Quando não definimos um usuário no nosso Dockerfile, o nosso container é executado por default como usuário ROOT. Não queremos um container vulnerável e com poderes de ROOT no nosso cluster. Essa combinação pode ser catastrófica!

Então, vamos definir um usuário para o nosso container.

# Dockerfile app covidtracker#satege1 build
FROM node:lts-alpine AS buildapp
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
#stage 2 run
FROM node:lts-alpine
EXPOSE 4000
WORKDIR /usr/src/app
COPY --from=buildapp /usr/src/app .
USER node
CMD ["node", "server.js"]

#6 Defina instruções de HEALTHCHECK

Como o nosso container recebe tráfego na porta 4000, nossa verificação de integridade deve ter certeza de que isso está acontecendo.

Uma verificação de integridade é configurada no Dockerfile usando a instrução HEALTHCHECK.

A utilização da instrução além de garantir a integridade do nosso container, evita falsos positivos com relação ao status do container.

Vamos adicionar a instrução do HEALTHCHECK no nosso Dockerfile:

#Dockerfile app covidtracker#satege1 build
FROM node:lts-alpine AS buildapp
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
#stage 2 run
FROM node:lts-alpine
EXPOSE 4000
WORKDIR /usr/src/app
COPY --from=buildapp /usr/src/app .
USER node
HEALTHCHECK CMD curl --fail http://localhost:4000/ || exit 1
CMD ["node", "server.js"]

Lembrando que a instrução de healthcheck é muito particular de cada aplicação, você deve personalizar de acordo com as características do seu workload.

#7 Utilize Lint no seu Dockerfile

Existem algumas ferramentas que permitem que façamos lint no nosso Dockerfile com o intuito de nos ajudar a construir uma imagem que segue as melhores práticas.

Dentre essas ferramentas podemos destacar a Haskell Dockerfile Linter, ou simplesmente Hadolint. Vou deixar aqui o link do repositório da solução no github:

#8 Utilize o arquivo oculto .dockerignore

A ideia com .dockerignore é muito simples, um arquivo onde podemos indicar artefatos, pastas, arquivos, extensões, dentre outros que não devem fazer parte da construção da nossa imagem.

#9 Valide sua imagem localmente

Ter condições de validar a existência de vulnerabilidades e/ou configurações incorretas na construção do nosso Dockerfile antes de enviá-lo para o pipeline dá a condição de sermos muito mais assertivos na correção de vulnerabilidades, pois não precisamos aguardar até que a política de segurança quebre a nossa pipeline.

Existe uma ferramenta chamada Dockle que nos permite rodar localmente e fazer uma espécie de benchmark de segurança no nosso Dockerfile.

Vamos ver abaixo a saída dessa solução na versão da imagem que falha com a nossa política de segurança:

Algumas das recomendações dessa ferramenta batem com as recomendações desse post inclusive.

Sério, sempre que possível procure validar localmente a sua imagem, isso poupa muito tempo na correção.

Vamos ver como fica essa análise na nossa nova imagem com as alterações de segurança que fizemos:

Essa foto já esta bem melhor quando comparada a nossa análise anterior. Aqui temos somente alerta informacional, algo que deve ser corrigido claro, mas que normalmente não requer a mesma urgência que outros alertas como container executando como ROOT.

Neste link você pode encontrar mais detalhes sobre o Dockle.

Ok, vamos ver se melhorou?

Depois que procuramos por baselines de segurança para imagens de container, alteramos o nosso Dockerfile seguindo as melhores práticas e validamos localmente nossa imagem vamos finalmente comitar as nossas alterações para o nosso repositório e rodar nossa pipeline.

Vamos ver como ficou nosso Dockerfile:

#Dockerfile app covidtracker#satege1 build
FROM node:lts-alpine AS buildapp
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
#stage 2 run
FROM node:lts-alpine
EXPOSE 4000
WORKDIR /usr/src/app
COPY --from=buildapp /usr/src/app .
USER node
HEALTHCHECK CMD curl --fail http://localhost:4000/ || exit 1
CMD ["node", "server.js"]

Finalmente o resultado da nossa pipeline:

Bem, não é o que a gente esperava. Vamos dar uma olhada no novo relatório de segurança, para ver se avançamos em alguma correção:

Se levarmos em consideração que começamos o jogo com 293 vulnerabilidades, tivemos um gigantesco avanço, porém ainda estamos falhando na nossa política de segurança. Por quê?

#10 Sempre atualize as suas imagens

A última dica desse post é: sempre atualize a sua imagem, ou seja, seu Dockerfile.

Precisamos garantir que os componentes que estamos utilizando estão na sua versão mais estável. Muita atenção a dica aqui não é simplesmente para usar a tag latest, está longe disso. Navegue no repositório oficial da imagem base que está usando na guia de tags e veja quais são as mais estáveis para uso.

Vamos corrigir isso:

Alterando o nosso Dockerfile:

#Dockerfile app covidtracker#satege1 build
FROM node:lts-alpine AS buildapp
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
#stage 2 run
FROM node:lts-alpine3.13
EXPOSE 4000
WORKDIR /usr/src/app
COPY --from=buildapp /usr/src/app .
USER node
HEALTHCHECK CMD curl --fail http://localhost:4000/ || exit 1
CMD ["node", "server.js"]

Vamos comitar a nossa alteração e verificar o resultado da nossa pipeline:

Vamos ver como ficou o relatório de segurança agora:

Além de passar na política de segurança, garantimos que o nosso container está seguro e em Compliance de acordo com a política de segurança.

É importante lembrar que isso não é uma receita de bolo, e sim algumas técnicas que podemos adotar para garantir a entrega segura de containers e consequentemente de aplicações.

E o mais importante desmistificar a correção de vulnerabilidades, vimos que com poucos ajustes podemos entregar um produto seguro e com qualidade.

Recomendação:

Mais recentemente o João Freire, também conhecido na comunidade como p0ssuidao, fez uma palestra no DevOps Extreme onde ele mostra o impacto que um container vulnerável pode causar num cluster K8S.

A palestra está sensacional e você pode conferir o conteúdo completo neste link.

Por esse post é isso pessoal. Críticas e/ou sugestões são sempre bem-vindas. Abraço!

Referências:

--

--

Iluiz Sousa
Iluiz Sousa

Written by Iluiz Sousa

Tentando me tornar o que chamam de engenheiro DevOps.

No responses yet