Como adicionar ou remover capacidades usando comandos Docker

DockerBeginner
Pratique Agora

Introdução

Docker é uma ferramenta poderosa para a conteinerização de aplicações, e compreender como gerenciar as capacidades dos contêineres é crucial para otimizar a segurança e o desempenho. Este tutorial irá guiá-lo através do processo de adição e remoção de capacidades usando comandos Docker, ajudando você a adaptar o ambiente do seu contêiner às necessidades específicas.

Neste laboratório, você aprenderá o que são as capacidades do Docker, como elas aprimoram a segurança do contêiner e como gerenciá-las efetivamente. Ao final deste tutorial, você será capaz de adicionar e remover capacidades de seus contêineres Docker com confiança.

Compreendendo as Capacidades do Docker

As capacidades do Docker são um recurso de segurança que permite conceder ou revogar permissões específicas do kernel Linux a um contêiner. Antes de começarmos a experimentar com capacidades, vamos entender o que elas são e por que são importantes.

O que são as Capacidades do Docker?

As capacidades no Docker são baseadas no sistema de capacidades do kernel Linux, que divide os privilégios tradicionalmente associados ao usuário root em unidades distintas. Essa abordagem é mais segura do que o modelo tradicional de privilégio root "tudo ou nada".

Por padrão, os contêineres Docker são executados com um conjunto limitado de capacidades, fornecendo um equilíbrio razoável entre funcionalidade e segurança. No entanto, pode ser necessário adicionar ou remover capacidades com base nos requisitos da sua aplicação.

Vamos examinar as capacidades padrão de um contêiner Docker. Execute o seguinte comando para iniciar um contêiner e visualizar suas capacidades:

docker run --rm -it ubuntu:22.04 capsh --print

Você deve ver uma saída semelhante a esta:

Current: 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=ep
Bounding set =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
Ambient set =
Current IAB: 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=ep
Securebits: 00/0x0/1'b0
 secure-noroot: no (unlocked)
 secure-no-suid-fixup: no (unlocked)
 secure-keep-caps: no (unlocked)
 secure-no-ambient-raise: no (unlocked)
uid=0(root) gid=0(root) groups=0(root)

Esta saída mostra as capacidades concedidas ao contêiner por padrão. Essas capacidades controlam o que o contêiner pode fazer dentro do sistema.

Por que as Capacidades são Importantes

Gerenciar adequadamente as capacidades do Docker é crucial para:

  1. Segurança Aprimorada: Ao limitar as capacidades, você reduz os danos potenciais se um contêiner for comprometido.
  2. Controle Granular: Você pode permitir operações privilegiadas específicas sem conceder acesso root completo.
  3. Princípio do Mínimo Privilégio: Os contêineres devem ter apenas as capacidades de que precisam para funcionar corretamente.

Em seguida, vamos explorar como adicionar capacidades aos contêineres Docker.

Adicionando Capacidades aos Contêineres Docker

Nesta etapa, aprenderemos como adicionar capacidades específicas a um contêiner Docker usando a flag --cap-add. Isso é útil quando sua aplicação requer certos privilégios que não estão incluídos no conjunto padrão.

Sintaxe Básica para Adicionar Capacidades

A sintaxe básica para adicionar uma capacidade a um contêiner Docker é:

docker run --cap-add=<CAPABILITY> <IMAGE> <COMMAND>

Vamos tentar adicionar a capacidade NET_ADMIN, que permite ao contêiner executar várias operações relacionadas à rede:

docker run --rm -it --cap-add=NET_ADMIN ubuntu:22.04 capsh --print

A saída mostrará que a capacidade NET_ADMIN foi adicionada ao contêiner:

Current: 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,cap_net_admin=ep

Observe a adição de cap_net_admin no final da lista de capacidades.

Adicionando Múltiplas Capacidades

Frequentemente, pode ser necessário adicionar várias capacidades a um contêiner. Você pode fazer isso especificando a flag --cap-add várias vezes:

docker run --rm -it --cap-add=NET_ADMIN --cap-add=SYS_TIME ubuntu:22.04 capsh --print

Este comando adiciona as capacidades NET_ADMIN e SYS_TIME ao contêiner. A capacidade SYS_TIME permite que o contêiner modifique o relógio do sistema.

Exemplo Prático: Modificando Interfaces de Rede

Para demonstrar um uso prático de capacidades, vamos criar um contêiner com a capacidade NET_ADMIN e tentar modificar as configurações da interface de rede:

docker run --rm -it --cap-add=NET_ADMIN ubuntu:22.04 /bin/bash

Você está agora dentro do contêiner com um shell bash. Vamos instalar o pacote iproute2 para trabalhar com interfaces de rede:

apt-get update && apt-get install -y iproute2

Agora, tente criar uma interface de rede fictícia:

ip link add dummy0 type dummy
ip link show dummy0

Você deve ver a saída mostrando a interface de rede fictícia recém-criada:

6: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 2a:d5:cd:70:91:f4 brd ff:ff:ff:ff:ff:ff

Esta operação falharia sem a capacidade NET_ADMIN. Você pode sair do contêiner digitando exit.

Visualizando as Capacidades do Contêiner

Para inspecionar as capacidades de um contêiner em execução, você pode usar o comando docker inspect. Primeiro, vamos iniciar um contêiner com capacidades adicionadas no modo detached:

docker run -d --name cap-test --cap-add=NET_ADMIN ubuntu:22.04 sleep 3600

Agora, inspecione o contêiner para visualizar suas capacidades:

docker inspect cap-test | grep -A 20 CapAdd

A saída mostrará que a capacidade NET_ADMIN foi adicionada:

            "CapAdd": [
                "NET_ADMIN"
            ],

Lembre-se de limpar após esta etapa:

docker stop cap-test
docker rm cap-test

Compreender como adicionar capacidades aos contêineres Docker oferece mais controle sobre o que seus contêineres podem fazer, mantendo a segurança.

Removendo Capacidades de Contêineres Docker

Nesta etapa, aprenderemos como remover capacidades de contêineres Docker usando a flag --cap-drop. Esta é uma prática de segurança importante que segue o princípio do menor privilégio - os contêineres devem ter apenas as capacidades de que absolutamente precisam.

Sintaxe Básica para Remover Capacidades

A sintaxe básica para remover uma capacidade de um contêiner Docker é:

docker run --cap-drop=<CAPABILITY> <IMAGE> <COMMAND>

Vamos tentar remover a capacidade CHOWN, que permite alterar a propriedade do arquivo:

docker run --rm -it --cap-drop=CHOWN ubuntu:22.04 capsh --print

Na saída, você notará que cap_chown não está mais listado entre as capacidades:

Current: 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=ep

Removendo Múltiplas Capacidades

Você pode remover várias capacidades especificando a flag --cap-drop várias vezes:

docker run --rm -it --cap-drop=CHOWN --cap-drop=NET_RAW ubuntu:22.04 capsh --print

Este comando remove as capacidades CHOWN e NET_RAW do contêiner.

Exemplo Prático: Testando Restrições de Capacidade

Vamos criar um contêiner com a capacidade CHOWN removida e tentar alterar a propriedade do arquivo:

docker run --rm -it --cap-drop=CHOWN ubuntu:22.04 /bin/bash

Dentro do contêiner, vamos criar um arquivo de teste e tentar alterar sua propriedade:

touch test_file
ls -l test_file
chown nobody:nogroup test_file

Você deve ver uma mensagem de erro indicando que a operação não é permitida:

chown: changing ownership of 'test_file': Operation not permitted

Isso demonstra que a remoção da capacidade CHOWN impede que o contêiner altere a propriedade do arquivo, mesmo que o contêiner esteja sendo executado como root. Digite exit para sair do contêiner.

Usando --cap-add e --cap-drop

Você pode usar as flags --cap-add e --cap-drop no mesmo comando para controlar com precisão as capacidades do seu contêiner:

docker run --rm -it --cap-add=NET_ADMIN --cap-drop=CHOWN ubuntu:22.04 capsh --print

Este comando adiciona a capacidade NET_ADMIN enquanto remove a capacidade CHOWN.

Removendo Todas as Capacidades e Adicionando as Específicas

Para máxima segurança, você pode remover todas as capacidades e, em seguida, adicionar apenas as específicas de que sua aplicação precisa:

docker run --rm -it --cap-drop=ALL --cap-add=NET_BIND_SERVICE ubuntu:22.04 capsh --print

Este comando cria um contêiner com apenas a capacidade NET_BIND_SERVICE, que permite a ligação a portas privilegiadas (abaixo de 1024).

Testando um Contêiner com Todas as Capacidades Removidas

Vamos criar um contêiner com todas as capacidades removidas e observar as restrições:

docker run -d --name no-caps --cap-drop=ALL ubuntu:22.04 sleep 3600

Agora, vamos anexar ao contêiner e tentar executar várias operações:

docker exec -it no-caps /bin/bash

Dentro do contêiner, tente fazer ping em um host externo:

apt-get update && apt-get install -y iputils-ping
ping -c 1 google.com

Você provavelmente verá um erro porque o contêiner não tem as capacidades necessárias para criar sockets de rede brutos necessários para o ping.

Saia do contêiner digitando exit e, em seguida, limpe:

docker stop no-caps
docker rm no-caps

Ao entender como remover capacidades de contêineres Docker, você pode aprimorar significativamente a segurança de suas aplicações em contêineres, limitando estritamente o que cada contêiner pode fazer.

Combinando o Gerenciamento de Capacidades com as Melhores Práticas de Segurança

Nesta etapa final, exploraremos como combinar o gerenciamento de capacidades do Docker com outras melhores práticas de segurança para criar contêineres seguros e com o mínimo de privilégios.

Criando um Contêiner com um Perfil de Capacidades Personalizado

Vamos criar um exemplo mais complexo que combina o gerenciamento de capacidades com outros recursos de segurança:

docker run -d --name secure-container \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --read-only \
  --tmpfs /tmp \
  ubuntu:22.04 sleep 3600

Este comando:

  • Remove todas as capacidades
  • Adiciona apenas a capacidade NET_BIND_SERVICE
  • Torna o sistema de arquivos do contêiner somente leitura
  • Cria um sistema de arquivos temporário em /tmp para operações de escrita

Vamos inspecionar este contêiner para ver sua configuração:

docker inspect secure-container | grep -A 5 CapAdd
docker inspect secure-container | grep -A 5 CapDrop
docker inspect secure-container | grep ReadonlyRootfs

Você deve ver a saída confirmando essas configurações de segurança:

            "CapAdd": [
                "NET_BIND_SERVICE"
            ],
            "CapDrop": [
                "ALL"
            ],
"ReadonlyRootfs": true,

Testando as Restrições

Vamos nos conectar ao nosso contêiner seguro e testar as restrições:

docker exec -it secure-container /bin/bash

No contêiner, tente modificar um arquivo do sistema:

echo "test" > /etc/test

Você deve ver um erro porque o sistema de arquivos é somente leitura:

bash: /etc/test: Read-only file system

Agora, tente escrever no diretório /tmp:

echo "test" > /tmp/test
cat /tmp/test

Isso deve funcionar porque montamos um tmpfs gravável em /tmp:

test

Saia do contêiner digitando exit.

Usando Capacidades com Usuários Não-Root

Para segurança adicional, você pode executar contêineres como usuários não-root, ainda gerenciando capacidades. Primeiro, vamos criar um novo contêiner que combina um usuário não-root com capacidades específicas:

docker run -d --name nonroot-container \
  --cap-drop=ALL \
  --cap-add=NET_BIND_SERVICE \
  --user 1000:1000 \
  ubuntu:22.04 sleep 3600

Observe que, embora tenhamos adicionado a capacidade NET_BIND_SERVICE e estejamos executando como um usuário não-root, as capacidades do Linux são aplicadas apenas aos processos em execução como root por padrão. Para permitir que usuários não-root usem capacidades, configurações adicionais como binários setuid ou capacidades ambientais seriam necessárias.

Capacidades em docker-compose

Se você usa docker-compose para gerenciar vários contêineres, pode especificar capacidades em seu arquivo docker-compose.yml:

version: "3"
services:
  webapp:
    image: ubuntu:22.04
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /tmp

Isso fornece uma maneira consistente de gerenciar capacidades em suas implantações de contêiner.

Limpeza

Vamos limpar os contêineres que criamos:

docker stop secure-container nonroot-container
docker rm secure-container nonroot-container

Resumo das Melhores Práticas

Aqui estão algumas melhores práticas para gerenciar as capacidades do Docker:

  1. Remova todas as capacidades e adicione apenas o que você precisa
  2. Combine o gerenciamento de capacidades com outros recursos de segurança:
    • Sistema de arquivos somente leitura
    • Usuários não-root
    • Perfis Seccomp
    • AppArmor ou SELinux
  3. Audite regularmente as capacidades do contêiner
  4. Mantenha o Docker e as imagens do contêiner atualizados
  5. Use ferramentas de verificação de vulnerabilidade de contêiner

Ao seguir essas práticas, você pode melhorar significativamente a segurança de seus contêineres Docker.

Resumo

Neste laboratório, você aprendeu como gerenciar efetivamente as capacidades dos contêineres Docker para aprimorar a segurança e a funcionalidade. Aqui está um resumo do que você realizou:

  1. Você entendeu o que são as capacidades do Docker e por que elas são importantes para a segurança dos contêineres.
  2. Você aprendeu como adicionar capacidades aos contêineres usando a flag --cap-add, permitindo que os contêineres executem operações privilegiadas específicas.
  3. Você praticou a remoção de capacidades com a flag --cap-drop, implementando o princípio do menor privilégio.
  4. Você explorou as melhores práticas para combinar o gerenciamento de capacidades com outros recursos de segurança para criar ambientes de contêiner seguros.

Ao aplicar essas técnicas, você pode criar contêineres que têm precisamente as permissões de que precisam, nem mais, nem menos. Essa abordagem reduz significativamente a superfície de ataque potencial de suas aplicações em contêineres, garantindo que elas tenham a funcionalidade necessária para operar corretamente.

Continue explorando os recursos de segurança do Docker e lembre-se de auditar e atualizar regularmente as configurações de seus contêineres para manter uma forte postura de segurança em seus ambientes em contêineres.