Como Gerenciar Permissões no Docker?

DockerBeginner
Pratique Agora

Introdução

O gerenciamento de permissões no Docker é um aspecto crucial para gerenciar suas aplicações em contêineres. Este tutorial irá guiá-lo através da compreensão das permissões de arquivos Docker, da configuração de permissões em contêineres Docker e da exploração das melhores práticas para o gerenciamento de permissões. Ao final, você terá o conhecimento necessário para lidar efetivamente com permissões em seus projetos baseados em Docker.

Neste laboratório, você aprenderá como as permissões de contêineres Docker funcionam, como criar e usar usuários não-root em contêineres e como gerenciar permissões ao compartilhar dados entre o host e os contêineres.

Compreendendo as Permissões Padrão do Docker

No Docker, entender como as permissões funcionam é fundamental para manter contêineres seguros. Vamos começar explorando as configurações de permissões padrão em contêineres Docker.

Verificando o Usuário Padrão no Docker

Por padrão, o Docker executa processos dentro de contêineres como o usuário root. Isso pode levar a potenciais problemas de segurança se o contêiner for comprometido. Para ver esse comportamento, vamos criar um contêiner simples e verificar qual usuário executa os processos.

Primeiro, certifique-se de estar no diretório do seu projeto:

cd ~/project

Agora, execute um contêiner Ubuntu básico e verifique o usuário atual:

docker run -it --rm ubuntu:22.04 whoami

Você deve ver a seguinte saída:

root

Isso confirma que o Docker usa o usuário root por padrão. Agora, vamos verificar o ID do usuário (UID) e o ID do grupo (GID):

docker run -it --rm ubuntu:22.04 id

A saída deve ser semelhante a:

uid=0(root) gid=0(root) groups=0(root)

O uid=0 e gid=0 indicam que o contêiner está sendo executado como o usuário e grupo root, que têm acesso total a todos os recursos no contêiner.

Explorando as Permissões de Arquivos Dentro de um Contêiner

Vamos examinar como as permissões de arquivos funcionam dentro de um contêiner Docker. Crie um arquivo simples dentro de um contêiner e verifique suas permissões.

Primeiro, crie um contêiner que será executado em segundo plano:

docker run -d --name permissions-demo ubuntu:22.04 sleep 3600

Agora, vamos executar comandos dentro deste contêiner para criar um arquivo e verificar suas permissões:

docker exec permissions-demo touch /test-file
docker exec permissions-demo ls -l /test-file

Você deve ver uma saída semelhante a:

-rw-r--r-- 1 root root 0 May 15 12:34 /test-file

Observe que o arquivo pertence ao usuário e grupo root. Isso demonstra que todos os arquivos criados dentro de um contêiner Docker por padrão pertencem ao usuário root.

Vamos criar um diretório e verificar suas permissões também:

docker exec permissions-demo mkdir /test-directory
docker exec permissions-demo ls -ld /test-directory

A saída deve ser semelhante a:

drwxr-xr-x 2 root root 4096 May 15 12:35 /test-directory

Novamente, o diretório pertence ao usuário e grupo root.

Vamos limpar o contêiner antes de passar para a próxima etapa:

docker stop permissions-demo
docker rm permissions-demo

Isso demonstra o comportamento padrão do Docker em relação às permissões do usuário. Na próxima etapa, aprenderemos como criar e usar usuários não-root em contêineres Docker para melhorar a segurança.

Criando e Usando Usuários Não-Root no Docker

Executar aplicações como o usuário root dentro de contêineres Docker apresenta riscos de segurança. Se um invasor comprometer um contêiner em execução como root, ele poderá potencialmente obter privilégios elevados no sistema host. Nesta etapa, aprenderemos como criar e usar usuários não-root em contêineres Docker.

Criando um Dockerfile com um Usuário Não-Root

Vamos criar um Dockerfile que define um usuário não-root e o define como o usuário padrão para executar comandos.

Primeiro, crie um novo diretório para nosso projeto:

mkdir -p ~/project/non-root-user
cd ~/project/non-root-user

Agora, crie um Dockerfile usando o editor de texto nano:

nano Dockerfile

Adicione o seguinte conteúdo ao Dockerfile:

FROM ubuntu:22.04

## Crie um novo usuário chamado 'appuser' com o ID de usuário 1000
RUN useradd -m -u 1000 appuser

## Crie um diretório para a aplicação e defina a propriedade
RUN mkdir -p /app && chown -R appuser:appuser /app

## Defina o diretório de trabalho para /app
WORKDIR /app

## Mude para o usuário não-root
USER appuser

## Crie um arquivo de teste
RUN touch test-file.txt

## Comando para executar quando o contêiner iniciar
CMD ["bash", "-c", "echo 'Running as user:' && whoami && echo 'File ownership:' && ls -l test-file.txt && tail -f /dev/null"]

Salve o arquivo pressionando Ctrl+O, depois Enter e saia do editor com Ctrl+X.

Este Dockerfile faz o seguinte:

  1. Cria um novo usuário chamado appuser com UID 1000
  2. Cria um diretório para a aplicação e dá a propriedade ao appuser
  3. Define o diretório de trabalho para /app
  4. Muda para o usuário não-root para comandos subsequentes
  5. Cria um arquivo de teste que será propriedade do appuser
  6. Define um comando que exibirá informações de usuário e propriedade do arquivo

Construindo e Executando o Contêiner do Usuário Não-Root

Agora, vamos construir a imagem Docker a partir do nosso Dockerfile:

docker build -t non-root-image .

Você deve ver a saída indicando que o Docker está construindo a imagem. Depois que a construção for concluída, execute um contêiner a partir desta imagem:

docker run --name non-root-container -d non-root-image

Agora, vamos verificar a saída do nosso contêiner:

docker logs non-root-container

Você deve ver uma saída semelhante a:

Running as user:
appuser
File ownership:
-rw-r--r-- 1 appuser appuser 0 May 15 12:45 test-file.txt

Isso confirma que o contêiner está sendo executado como o usuário appuser e que o arquivo de teste é propriedade do appuser.

Testando as Permissões do Usuário Não-Root

Vamos nos conectar ao contêiner em execução e explorar as permissões do nosso usuário não-root:

docker exec -it non-root-container bash

Agora você deve estar dentro do contêiner como o appuser. Vamos verificar isso:

whoami

Você deve ver:

appuser

Verifique o ID do usuário e o ID do grupo:

id

A saída deve ser semelhante a:

uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)

Agora, tente criar um arquivo no diretório raiz:

touch /root-test-file

Você deve ver um erro de permissão negada:

touch: cannot touch '/root-test-file': Permission denied

Isso ocorre porque o usuário não-root não tem permissões de gravação no diretório raiz. No entanto, nosso usuário pode escrever no diretório /app:

touch /app/user-test-file
ls -l /app/user-test-file

Você deve ver que o novo arquivo é propriedade do appuser:

-rw-r--r-- 1 appuser appuser 0 May 15 12:50 /app/user-test-file

Saia do shell do contêiner:

exit

Vamos limpar o contêiner antes de passar para a próxima etapa:

docker stop non-root-container
docker rm non-root-container

Esta etapa demonstrou como criar e usar um usuário não-root em um contêiner Docker. Usar um usuário não-root ajuda a melhorar a segurança de suas aplicações em contêineres, restringindo as permissões disponíveis para a aplicação.

Gerenciando as Permissões de Montagem de Volume

Os volumes Docker permitem que você compartilhe dados entre o host e os contêineres. No entanto, gerenciar as permissões corretamente ao usar volumes é importante para evitar problemas. Nesta etapa, aprenderemos como lidar com as permissões ao usar montagens de volume.

Compreendendo os Problemas de Permissão de Montagem de Volume

O principal desafio com as montagens de volume Docker é que os IDs de usuário dentro do contêiner podem não corresponder aos IDs de usuário no sistema host. Isso pode levar a problemas de permissão ao acessar arquivos em volumes montados.

Vamos demonstrar esse problema com um exemplo simples.

Primeiro, crie um novo diretório no host que montaremos em um contêiner:

mkdir -p ~/project/host-data
cd ~/project/host-data

Crie um arquivo de teste neste diretório:

echo "This is a test file created on the host" > host-file.txt

Verifique a propriedade deste arquivo:

ls -l host-file.txt

Você deve ver que o arquivo pertence ao usuário labex (seu usuário atual no host):

-rw-r--r-- 1 labex labex 39 May 15 13:00 host-file.txt

Agora, vamos executar um contêiner que monta este diretório e tentar modificar o arquivo:

docker run -it --rm -v ~/project/host-data:/container-data ubuntu:22.04 bash

Você está agora dentro do contêiner. Vamos verificar a propriedade do arquivo montado:

ls -l /container-data/host-file.txt

Você pode ver uma saída semelhante a:

-rw-r--r-- 1 1000 1000 39 May 15 13:00 /container-data/host-file.txt

Observe que o arquivo mostra IDs numéricos em vez de nomes de usuário porque o contêiner não conhece o usuário labex do host.

Tente criar um novo arquivo no diretório montado:

echo "This is a test file created in the container" > /container-data/container-file.txt

Agora, verifique a propriedade deste novo arquivo:

ls -l /container-data/container-file.txt

Você deve ver:

-rw-r--r-- 1 root root 47 May 15 13:05 /container-data/container-file.txt

O arquivo pertence ao usuário root dentro do contêiner (já que estamos executando como root por padrão).

Saia do contêiner:

exit

Agora, verifique a propriedade de ambos os arquivos no host:

ls -l ~/project/host-data/

Você verá que o arquivo criado de dentro do contêiner pertence ao root no host:

-rw-r--r-- 1 root  root  47 May 15 13:05 container-file.txt
-rw-r--r-- 1 labex labex 39 May 15 13:00 host-file.txt

Isso pode levar a problemas de permissão se um usuário não-root no host precisar acessar ou modificar esses arquivos.

Resolvendo Problemas de Permissão de Volume

Existem várias maneiras de resolver problemas de permissão de volume. Vamos explorar algumas abordagens comuns.

Abordagem 1: Definir o Usuário no Dockerfile para Corresponder ao Usuário do Host

Crie um novo diretório para este exemplo:

mkdir -p ~/project/volume-permissions
cd ~/project/volume-permissions

Crie um novo Dockerfile:

nano Dockerfile

Adicione o seguinte conteúdo:

FROM ubuntu:22.04

## Crie um usuário com o mesmo UID que o usuário do host
RUN useradd -m -u 1000 appuser

## Crie o diretório do aplicativo e defina a propriedade
RUN mkdir -p /app/data && chown -R appuser:appuser /app

## Defina o diretório de trabalho
WORKDIR /app

## Mude para appuser
USER appuser

## Comando para executar
CMD ["bash", "-c", "echo 'I can write to the mounted volume' > /app/data/test.txt && tail -f /dev/null"]

Salve e saia do editor.

Construa a imagem:

docker build -t volume-permissions-image .

Crie um diretório host para teste:

mkdir -p ~/project/volume-permissions/host-data

Execute um contêiner com o volume montado:

docker run -d --name volume-test -v ~/project/volume-permissions/host-data:/app/data volume-permissions-image

Após um momento, verifique a propriedade do arquivo criado no host:

ls -l ~/project/volume-permissions/host-data/

Você deve ver que o arquivo pertence a um usuário com UID 1000, que deve corresponder ao UID do seu usuário host:

-rw-r--r-- 1 labex labex 35 May 15 13:15 test.txt

Essa abordagem funciona porque criamos um usuário no contêiner com o mesmo UID que o usuário do host.

Abordagem 2: Usando a Flag --user

Outra abordagem é usar a flag --user para especificar qual ID de usuário usar ao executar o contêiner.

Primeiro, limpe o contêiner anterior:

docker stop volume-test
docker rm volume-test

Agora, execute um contêiner com a flag --user:

docker run -d --name user-flag-test --user "$(id -u):$(id -g)" -v ~/project/volume-permissions/host-data:/data ubuntu:22.04 bash -c "echo 'Created with --user flag' > /data/user-flag-test.txt && sleep 3600"

Este comando executa o contêiner com seu ID de usuário e ID de grupo atuais.

Verifique a propriedade do novo arquivo:

ls -l ~/project/volume-permissions/host-data/user-flag-test.txt

Você deve ver que o arquivo pertence ao seu usuário host:

-rw-r--r-- 1 labex labex 23 May 15 13:20 user-flag-test.txt

Vamos limpar antes de passar para a próxima etapa:

docker stop user-flag-test
docker rm user-flag-test

Essas abordagens demonstram como gerenciar permissões ao usar volumes Docker. Ao corresponder os IDs de usuário entre o contêiner e o host, você pode evitar problemas de permissão ao compartilhar dados entre eles.

Implementando as Melhores Práticas de Permissão

Vamos colocar tudo o que aprendemos em prática, criando um exemplo mais realista de um contêiner Docker que segue as melhores práticas de permissão. Criaremos um contêiner de aplicação web simples que segue as melhores práticas de segurança para permissões.

Criando um Contêiner de Aplicação Web Seguro

Primeiro, crie um novo diretório para nosso exemplo:

mkdir -p ~/project/secure-app
cd ~/project/secure-app

Vamos criar uma aplicação web simples. Primeiro, crie um arquivo app.py:

nano app.py

Adicione o seguinte código Python para criar um servidor web Flask simples:

from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello from a secure container!'

@app.route('/whoami')
def whoami():
    return os.popen('id').read()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

Salve e saia do editor.

Agora, crie um arquivo requirements.txt para as dependências Python:

nano requirements.txt

Adicione o seguinte conteúdo:

flask==2.0.1

Salve e saia do editor.

Agora, crie um Dockerfile que segue as melhores práticas de permissão:

nano Dockerfile

Adicione o seguinte conteúdo:

FROM python:3.10-slim

## Crie um usuário não-root para executar a aplicação
RUN groupadd -g 1000 appgroup \
  && useradd -u 1000 -g appgroup -s /bin/bash -m appuser

## Defina o diretório de trabalho e crie os diretórios necessários
WORKDIR /app

## Copie os requisitos primeiro para aproveitar o cache do Docker
COPY requirements.txt .

## Instale as dependências como root
RUN pip install --no-cache-dir -r requirements.txt

## Copie o código da aplicação
COPY app.py .

## Crie um diretório de dados que a aplicação possa escrever
RUN mkdir -p /app/data \
  && chown -R appuser:appgroup /app

## Defina as permissões corretas
RUN chmod -R 755 /app

## Mude para o usuário não-root
USER appuser

## Exponha a porta em que o aplicativo será executado
EXPOSE 8080

## Comando para executar a aplicação
CMD ["python", "app.py"]

Salve e saia do editor.

Vamos construir a imagem Docker:

docker build -t secure-web-app .

Agora, execute o contêiner:

docker run -d --name secure-app -p 8080:8080 secure-web-app

Vamos testar a aplicação. Primeiro, verifique se o contêiner está em execução:

docker ps

Você deve ver seu contêiner secure-app na lista. Agora, use curl para testar a aplicação:

curl http://localhost:8080/

Você deve ver:

Hello from a secure container!

Vamos verificar com qual usuário a aplicação está sendo executada:

curl http://localhost:8080/whoami

Você deve ver uma saída semelhante a:

uid=1000(appuser) gid=1000(appgroup) groups=1000(appgroup)

Isso confirma que nossa aplicação está sendo executada como o appuser não-root.

Análise de Segurança

Vamos analisar as melhorias de segurança em nosso Dockerfile:

  1. Usuário não-root: Criamos um usuário dedicado (appuser) para executar a aplicação, reduzindo o risco caso o contêiner seja comprometido.

  2. Permissões mínimas: Definimos apenas as permissões necessárias para que a aplicação funcione.

  3. Propriedade clara: Todos os arquivos e diretórios no contêiner têm propriedade claramente definida.

  4. Estrutura de diretórios adequada: Criamos uma estrutura de diretórios dedicada para a aplicação e seus dados.

Essas práticas ajudam a garantir que, mesmo que um invasor consiga explorar uma vulnerabilidade na aplicação, ele terá acesso limitado ao contêiner e ao sistema host.

Implementando Permissões de Volume com o Aplicativo Seguro

Vamos adicionar um volume à nossa aplicação segura para demonstrar como lidar com permissões corretamente com volumes.

Primeiro, pare e remova o contêiner existente:

docker stop secure-app
docker rm secure-app

Crie um diretório de dados no host:

mkdir -p ~/project/secure-app/host-data

Defina as permissões corretas no diretório do host:

sudo chown 1000:1000 ~/project/secure-app/host-data

Agora, execute o contêiner com o volume montado:

docker run -d --name secure-app-with-volume \
  -p 8080:8080 \
  -v ~/project/secure-app/host-data:/app/data \
  secure-web-app

Vamos nos conectar ao contêiner e criar um arquivo no volume montado:

docker exec -it secure-app-with-volume bash

Agora, dentro do contêiner, crie um arquivo de teste no volume montado:

echo "Test file created from inside the container" > /app/data/container-file.txt

Verifique a propriedade do arquivo:

ls -l /app/data/container-file.txt

Você deve ver que o arquivo pertence ao appuser:

-rw-r--r-- 1 appuser appgroup 43 May 15 13:30 /app/data/container-file.txt

Saia do contêiner:

exit

Agora, verifique a propriedade do arquivo no host:

ls -l ~/project/secure-app/host-data/container-file.txt

Você deve ver que o arquivo pertence ao UID 1000 (que corresponde ao seu usuário host):

-rw-r--r-- 1 labex labex 43 May 15 13:30 container-file.txt

Isso demonstra que, com a configuração de permissão adequada, os arquivos criados dentro do contêiner em um volume montado terão a propriedade correta no host.

Vamos limpar antes de concluir:

docker stop secure-app-with-volume
docker rm secure-app-with-volume

Ao seguir essas melhores práticas para permissões Docker, você pode criar aplicações em contêineres seguras e confiáveis que gerenciam corretamente as permissões de arquivo, tanto dentro do contêiner quanto ao compartilhar dados com o sistema host.

Resumo

Neste laboratório, você aprendeu técnicas essenciais para gerenciar permissões em contêineres Docker:

  1. Compreendendo as Permissões Padrão do Docker: Você explorou como os contêineres Docker são executados como root por padrão e como isso pode levar a potenciais riscos de segurança.

  2. Criando e Usando Usuários Não-Root: Você aprendeu como criar usuários não-root personalizados em contêineres Docker, o que é uma prática recomendada de segurança crítica.

  3. Gerenciando as Permissões de Montagem de Volume: Você descobriu como lidar com permissões ao compartilhar dados entre o host e os contêineres usando volumes, abordando problemas comuns de permissão.

  4. Implementando as Melhores Práticas de Permissão: Você aplicou todos esses conceitos para criar um contêiner de aplicação web seguro que segue as melhores práticas de permissão do Docker.

Ao aplicar essas técnicas em seus projetos Docker, você pode melhorar significativamente a segurança e a confiabilidade de suas aplicações em contêineres. Lembre-se, executar contêineres com o mínimo de privilégios necessários é um princípio fundamental de segurança que deve sempre ser seguido.

Alguns pontos-chave:

  • Sempre use usuários não-root em seus contêineres
  • Corresponda os IDs de usuário entre o host e o contêiner ao usar volumes
  • Defina as permissões corretas para arquivos e diretórios
  • Siga o princípio do menor privilégio

Essas práticas ajudarão você a construir contêineres Docker mais seguros e a evitar problemas comuns relacionados a permissões.