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:
- Segurança Aprimorada: Ao limitar as capacidades, você reduz os danos potenciais se um contêiner for comprometido.
- Controle Granular: Você pode permitir operações privilegiadas específicas sem conceder acesso root completo.
- 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
/tmppara 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:
- Remova todas as capacidades e adicione apenas o que você precisa
- 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
- Audite regularmente as capacidades do contêiner
- Mantenha o Docker e as imagens do contêiner atualizados
- 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:
- Você entendeu o que são as capacidades do Docker e por que elas são importantes para a segurança dos contêineres.
- Você aprendeu como adicionar capacidades aos contêineres usando a flag
--cap-add, permitindo que os contêineres executem operações privilegiadas específicas. - Você praticou a remoção de capacidades com a flag
--cap-drop, implementando o princípio do menor privilégio. - 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.



