Executando verificação de segurança...
2

Processos em containers não devem ser executados como root

Esse artigo é uma tradução livre de Processes In Containers Should Not Run As Root por Marc Campbell.

tldr;

Um processo dentro de um container não deve rodar com privilégios de root, assuma isso como verdade.

Ao invés disse, crie um usuário em seu Dockerfile com um conhecido UID e GID, e execute seu processo usando esse usuário. Imagens construídas sobre esse padrão são mais fáceis de serem executadas de forma segura por limitar o acesso a determinados recursos.

Overview

Sistemas bem arquitetados aderem ao princípio do menor privilégio. Esse princípio diz que um aplicativo deve ter acesso apenas aos recursos necessários para executar a função necessária. Isso é muito importante quando desejamos fazer um sistema seguro. Seja malicioso ou por causa de erros, um processo pode ter consequências inesperadas em tempo de execução. Um dos melhores modos de se proteger contra qualquer acesso inesperado é conceder o mínimo privilégio para que um processo seja executado.

A maior dos processos conteinerizados são serviços de aplicações, sendo assim, elas não precisa de acesso como root. Enquanto o Docker precisa ser executado como root, os containers em si, não. Imagens Docker bem escritas, seguras e reutilizáveis ​​não devem ser executadas como root e devem fornecer um método previsível e fácil para limitar o acesso.

Por que isso é importante?

Lembre-se que um processo em execução em um container não é diferente de outro processo em execução no Linux, exceto que ele tem um pequeno pedaço de metadata informando que ele está sendo executado em um container.

Os containers são tão seguros quanto sua máquina host, portanto, qualquer coisa executada em um container deve ser tratada com a mesma consideração que qualquer coisa executada no próprio host.

Assim como você não faria (ou não deveria) executar qualquer coisa como root em seu servidor, você não deveria executar nada como root em um container em seu servidor. Executar binários que foram criados em outro lugar requer confiança, e o mesmo é verdadeiro para binários em containers.

Se um processo dentro de um container for executada como root por default, é possível mudar o uid e gid quando o container for iniciado. Sendo o criador da imagem, você deve usar como padrão um usuário diferente e facilitar a limitação de acesso para ele. Criando um usuário em seu Dockerfile, você está tornando não apenas ele seguro por padrão, mas também mais fácil de continuar seguro.

O exemplo abaixo mostrará o risco de executar um container como root. Vamos criar um arquivo no diretório /root, impedindo qualquer um diferente do root vê-lo:

marc@srv:~$ sudo -sroot@srv:~# cd /root
root@srv:~# echo "top secret stuff" >> ./secrets.txt 
root@srv:~# chmod 0600 secrets.txt
root@srv:/root# ls -l
total 4
-rw------- 1 root root 17 Sep 26 20:29 secrets.txtroot@srv:/root# exit
exitmarc@srv:~$ cat /root/secrets.txt
cat: /root/secrets.txt: Permission denied

Agora eu tenho um arquivo /root/secrets.txt que só o root consegue ver. Estou logado como um usuário normal(não root). Vamos criar uma imagem Docker a partir desse Dockerfile:

FROM debian:stretch
CMD ["cat", "/tmp/secrets.txt"]

E finalmente, vamos executar esse Dockerfile, montando um volume a partir do /root/secrets.txt que não possa ler o arquivo /tmp/secrets.txt dentro do container.

marc@srv:~$ docker run -v /root/secrets.txt:/tmp/secrets.txt <img>
top secret stuff

Mesmo sendo marc, o container está rodando como root e, portanto, tem acesso a tudo que o root tem acesso neste servidor. Isso não é o ideal; A execução de containers dessa maneira significa que todos os containers que você acessa do Docker Hub podem ter acesso total a tudo em seu servidor (dependendo de como você o executa).

Recomendação

A recomendação é criar um usuário com um uid conhecido no Dockerfile e rodar a aplicação usando esse usuário. O começo do Dockerfile deve seguir o seguinte padrão:

FROM <base image>RUN groupadd -g 999 appuser && \
    useradd -r -u 999 -g appuser appuser
USER appuser... <rest of Dockerfile> ...

Usando esse padrão é fácil executar um container no contexto do usuário/grupo com o mínimo de privilégios necessários.

Por exemplo, adicionarei isso ao meu Dockerfile de cima, e o executarei novamente. Meu arquivo ficou assim:

FROM debian:stretchRUN groupadd -g 999 appuser && \
    useradd -r -u 999 -g appuser appuser
USER appuserCMD ["cat", "/tmp/secrets.txt"]

Executando esse container com o mesmo comando de antes:

marc@srv:~$ docker run -v /root/secrets.txt:/tmp/secrets.txt <img>
cat: /tmp/secrets.txt: Permission denied

Agora, o comportamento padrão do container é que ele não terá privilégios de root no host.

Reusando outras imagens

Imagens Docker são incríveis por serem reutilizáveis, mas quando o FROM de uma imagem é executado através de um usuário normal, seu container herdará esse usuário. Se você precisar criar sua própria imagem ou executar operações como root tenha certeza que USER root esteja próximo do topo de seu Dockerfile. Então FROM appuser para torná-lo utilizável.

Executando outros containers como usuários normais (non-root)

Imagens Docker são feitas para serem portáteis, e é normal obter outras imagens do Docker Hub. Algumas delas (imagens oficiais) seguirão as melhores práticas e serão executadas como usuários normais (non-root), mas muitas imagens não fazem isso. Muitas simplesmente executam as coisas como root e deixam pra você descobrir como executar as coisas com segurança. Existem opções que permitirão você rodar uma imagem de forma segura que não foi criada por você.

Criando outra imagem

Primeiro, uma opção é criar outra imagem usando a imagem original com a camada FROM. Então você poderá criar um usuário, e copiar o ENTRYPOINT origin e diretrizes do CMD para sua própria imagem. Isso resultará em uma nova imgem que seguirá as boas práticas citadas aqui, garantindo que sua execução ocorra de forma segura. O que pode incomodar nesse ponto é que você precisará rebuildar sua imagem quando a imagem base for atualizada. Você terá que configurar um processo para fazer esse rebuild quando a imagem base for rebuildada.

Especificando um uid quando iniciando um container

Finalmente você pode criar um usuário no host e passar o uid pro Docker quando o container for iniciado. Por exemplo, revisitando o exemplo do Dockerfile:

FROM debian:stretch
CMD ["cat", "/tmp/secrets.txt"]

Eu consigo executar esse container com ou sem o parâmetro user_id e ver os diferentes resultados(o user_id 1001 representa minha conta atual nesse servidor):

$ docker run --user 1001 -v /root/secrets.txt:/tmp/secrets.txt <img>
cat: /tmp/secrets.txt: Permission denied$ docker run -v /root/secrets.txt:/tmp/secrets.txt <img>
top secret stuff

Isso funciona e é a mesma coisa que criar um usuário no Dockerfile, mas requer que o usuário opte por executar o container com segurança. Especificar um usuário normal (non-root) no Dockerfile fará com que o container seja executado com segurança por padrão.

Para leitura futura

Understanding how uid and gid work in Docker containers

Official reference to the Docker Run command (Docker)

Docker Security

— -

Se você está usando Docker para lançar uma aplicação SaaS, você deveria verificar www.replicated.com para poder lançar uma versão corporativa e instalável do seu produto.

Carregando publicação patrocinada...
1

Interessante.
Uma dúvida:
Se o usuário malicioso, tem acesso ao servidor e tem permissões pra usar o docker.
O que vai impedí-lo de criar o container com root pra burlar a segurança?