Por Fran Lauriano

Foi-se o tempo em que um desenvolvedor fazia seu trabalho sem pensar na segurança  da informação como meta fundamental a qualquer projeto. O desenvolvedor precisa saber que uma falha de segurança pode comprometer, senão a imagem de uma empresa, o próprio negócio dela. A possibilidade de vazamentos de informações sensíveis, como número de cartão de crédito dos clientes, é hoje uma das maiores preocupações dos gestores de negócios. Além, é claro, das fraudes de prejuízo financeiro direto às próprias empresas. Justamente por isso, cada vez mais, a segurança deve ser pensada em todos os níveis de um projeto, desde o desenvolvimento do código até a infraestrutura onde o mesmo irá rodar, o que torna o trabalho da equipe ainda mais minucioso.

São muitas as metodologias e ferramentas disponíveis para auxiliar o trabalho do desenvolvedor. E o Docker é uma delas. Sua metodologia de modularidade torna, tanto as alterações, quanto o controle de versões, mais eficiente e seguro. Já que a containerização permite desativar uma parte de uma aplicação, seja para reparo ou atualização, sem a interromper totalmente.

Porém, é preciso usar o Docker aliado a boas práticas de segurança, sobretudo porque ao instalar o Docker e utilizar suas imagens no servidor, abre-se mais uma janela para a exploração de vulnerabilidades. Desse modo, nosso objetivo aqui é detalhar 8 (oito) medidas para mitigar a exploração de vulnerabilidades usando o Docker. Entre elas, apresentamos desde boas práticas na criação de imagens, até a execução dos containers. Vale salientar que esse post é destinado a pessoas que já utilizam o Docker, ou que ao menos tenham algum conhecimento a respeito dele, posto que o mesmo priva-se de explicações mais básicas. Para informações iniciais sobre o que é e para que serve o Docker, sugerimos a leitura do tema no blog da RedHat.

  1. Use imagens confiáveis

Uma das grandes vantagens do uso do Docker é a criação de imagens a partir de uma  já existente. Sendo assim, é preciso ter cuidado com as imagens base utilizadas, pois muitas delas podem estar mal configuradas, ou pior, podem conter malwares. Para mitigar essa possibilidade, é indispensável o uso de imagens autênticas, disponibilizadas no Docker Hub. É também crucial habilitar o recurso Docker Content Trust responsável por validar as imagens, reconhecendo sua autenticidade; o que deve ser feito através do seguinte comando:

$ export DOCKER_CONTENT_TRUST=1

 

Deste modo, ao se tentar baixar uma imagem não assinada, a seguinte mensagem é apresentada:

$ docker pull mongoclient/mongoclient

Using default tag: latest

Error: remote trust data does not exist for docker.io/mongoclient/mongoclient: notary.docker.io does not have trust data for docker.io/mongoclient/mongoclient

   

  2. Mantenhas os containers atualizados

Manter os containers Docker atualizados deve evitar vulnerabilidades de segurança, já que as atualizações podem incluir patches essenciais. Porém, o simples uso da taglatest’ não é uma boa prática, já que ela baixará sempre a última versão disponível naquele momento, o que pode ‘quebrar’ sua aplicação. Por isso, o ideal é revisar seu Dockerfile rotineiramente e atualizá-lo com versões específicas.

  3. Não utilize o modo privilegiado

O container configurado no modo ‘privilegiado’ possui acesso root aos recursos da máquina host. Sendo assim, caso um atacante consiga acesso ao container, ele poderá acessar a máquina host com usuário root. Na prática, não é preciso se preocupar com isso, pois, por padrão, essa configuração vem desabilitada. Porém, é importante deixar claro que, ao criar o container deve-se evitar o uso da flagprivileged que ativa tal recurso. Caso queira verificar se um container está no modo privilegiado, utilize o seguinte comando:

$ docker inspect --format='{{.HostConfig.Privileged}}' [container_name|container_id]

 

Para rodar um container sem privilégio:

$ docker run -d --name noprivileged -it alpine

$ docker inspect --format='{{.HostConfig.Privileged}}' noprivileged




false

 

Para ativar o modo privilegiado:

$ docker run -d --privileged --name privileged -it alpine

$ docker inspect --format='{{.HostConfig.Privileged}}' privileged




true

 

  4. Não execute processos em containers como root

O uso de containers permite o isolamento de aplicações, porém o docker pode oferecer um isolamento incompleto, já que os recursos da máquina host são compartilhados com o container. Sendo assim, a execução de aplicações como root dentro do container pode viabilizar a exploração de vulnerabilidades do kernel ou do daemon do Docker, abrindo espaço para que um invasor possa escalar o acesso de dentro do container para fora, permitindo, desse modo, acesso à máquina host. Por isso, é recomendado especificar um usuário não root para execução dos processos dentro do container. Isso pode ser feito usando o comando USER. Segue um exemplo baseado em imagem alpine, onde é criado um usuário que escreve a mensagem “Hello World!” num arquivo e a exibe na tela:

FROM alpine:3.13

RUN adduser -D alpine

USER alpine

RUN echo "Hello World!" > /tmp/hello.txt

CMD ["cat", "/tmp/hello.txt"]

 

  5. Limite as capabilities de um container

Os containers docker vêm com algumas capabilities habilitadas. As capabilities do Linux permitem que processos realizem operações privilegiadas que deveriam estar limitadas apenas ao usuário root. Dessa forma, controlar o nível de privilégio de acesso de um usuário não é suficiente. Por isso, é preciso desabilitar esses recursos. Para exemplificar isto, é possível observar as capabilities de uma distribuição alpine, usando a seguinte Dockerfile:

FROM alpine:3.13

RUN adduser -D alpine

RUN apk update && apk add libcap

CMD ["getpcaps", "1"]

 

Mesmo sendo executado com um usuário sem root, o container ainda tem permissões sobre vários recursos:

$ docker run --rm --user alpine -it capabilities




1: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=i

 

Para desativar esses recursos, utiliza-se o parâmetro –cap-drop ALL:

docker run --cap-drop ALL --user alpine --rm -i -t capabilities




1: =

 

  6. Não coloque senhas em Dockerfile

Colocar senha no Dockerfile ou em código fonte utilizado na geração de imagens, além de não ser uma boa prática de segurança, torna a receita Dockerfile não reutilizável. Para melhor manipular essas informações sensíveis, pode-se usar o Docker Secrets ou o Kubernetes Secrets. Utilizando esses recursos, as senhas ficam em memória apenas no tempo de execução; desse modo, assim que o container for paralisado, essas informações somem da memória.

  7. Combine o uso de imagens leves com múltiplos estágios de build

A utilização de imagens pequenas, além de acelerar a etapa de criação da imagem, traz um ganho de segurança, já que elas diminuem as janelas de exploração de vulnerabilidades. Além disso, é possível combinar imagens leves com múltiplos estágios de build do Dockerfile, usando a tag de marcação FROM. Com isso, pode-se copiar um artefato de um estágio para outro conforme sua utilidade, deixando a imagem final apenas com o básico para seu funcionamento. Veja, no exemplo a seguir, uma receita Dockerfile para compilação e execução de uma aplicação em golang:

FROM golang:1.15.7-alpine3.13 as builder

RUN adduser -D deployer

WORKDIR /go/src/my-app

COPY api .

RUN go mod download

RUN CGO_ENABLED=0 go build -o build//my-app cmd/main.go




FROM scratch as /my-app

COPY --from=builder /etc/passwd /etc/passwd

COPY --from=builder /go/src//my-app/build//my-app .

USER deployer

CMD [".//my-app"]

  8. Faça uso de ferramentas de auditoria

Ferramentas de auditoria podem ser grandes aliadas para identificar más configurações no Docker. O Docker Bench for Security é uma dessas ferramentas, e segue as recomendações CIS Docker Benchmark. Essa aplicação possui 3 níveis de alerta: WARN, INFO e PASS. Os primeiros são críticos e devem ser corrigidos; os segundos, apesar de não serem críticos, não devem ser ignorados; já os últimos,  sinalizam que está tudo certo. Veja um exemplo:

CONCLUSÃO

Uma boa forma de se manter seguro com o uso do Docker, é, portanto, não esquecer destas 8 práticas seguras. A lembrar:

  1. Use imagens confiáveis;
  2. Mantenha os containers atualizados;
  3. Não utilize o modo privilegiado;
  4. Não execute processos em containers como root;
  5. Limite as capabilities de um container;
  6. Não coloque senhas em Dockerfile ou código fonte;
  7. Combine o uso de imagens leves com múltiplos estágios de build;
  8. Faça uso de ferramentas de auditoria.

 

REFERÊNCIAS

https://www.cisecurity.org/benchmark/docker/

https://docs.docker.com/engine/security/rootless/

https://docs.docker.com/engine/security/trust/

https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/

https://docs.docker.com/engine/swarm/secrets/

https://docs.docker.com/develop/develop-images/dockerfile_best-practices/

https://man7.org/linux/man-pages/man7/capabilities.7.html

https://kubernetes.io/docs/concepts/policy/pod-security-policy/#users-and-groups

https://github.com/docker/docker-bench-security

https://docs.docker.com/engine/security/