DevOps & CI/CD

Como Usar Segurança em Containers Docker: Rootless, Capabilities e Scanning em Produção

13 min de leitura

Como Usar Segurança em Containers Docker: Rootless, Capabilities e Scanning em Produção

Segurança em Containers Docker: Rootless, Capabilities e Scanning Quando falamos em segurança em containers, é essencial compreender que estamos lidando com um ambiente que compartilha o kernel do host. Isso significa que uma vulnerabilidade em um container pode potencialmente afetar o sistema inteiro. O Docker oferece várias camadas de proteção, e três delas são fundamentais: executar containers sem privilégios root, limitar capabilities do sistema operacional e realizar scanning de vulnerabilidades em imagens. Este artigo aborda cada uma dessas estratégias de forma prática e detalhada. Docker Rootless: Executando Containers sem Privilégios Root Entendendo o Problema do Root em Containers Por padrão, processos dentro de um container executam como root (UID 0). Isso parecia conveniente historicamente, mas representa um risco significativo. Se um atacante conseguir escapar do container, ele terá acesso root no host. O Docker Rootless é uma abordagem que executa tanto o daemon do Docker quanto os containers com um usuário não-root, criando uma camada adicional de segurança através de

<h2>Segurança em Containers Docker: Rootless, Capabilities e Scanning</h2>

<p>Quando falamos em segurança em containers, é essencial compreender que estamos lidando com um ambiente que compartilha o kernel do host. Isso significa que uma vulnerabilidade em um container pode potencialmente afetar o sistema inteiro. O Docker oferece várias camadas de proteção, e três delas são fundamentais: executar containers sem privilégios root, limitar capabilities do sistema operacional e realizar scanning de vulnerabilidades em imagens. Este artigo aborda cada uma dessas estratégias de forma prática e detalhada.</p>

<h2>Docker Rootless: Executando Containers sem Privilégios Root</h2>

<h3>Entendendo o Problema do Root em Containers</h3>

<p>Por padrão, processos dentro de um container executam como root (UID 0). Isso parecia conveniente historicamente, mas representa um risco significativo. Se um atacante conseguir escapar do container, ele terá acesso root no host. O Docker Rootless é uma abordagem que executa tanto o daemon do Docker quanto os containers com um usuário não-root, criando uma camada adicional de segurança através de user namespaces.</p>

<p>A diferença é fundamental: em um Docker padrão, o usuário root dentro do container é mapeado para o usuário root do host. No Docker Rootless, o usuário root do container é mapeado para um usuário normal (como <code>dockremap</code>) no host, eliminando esse privilégio.</p>

<h3>Instalando e Configurando Docker Rootless</h3>

<p>A instalação do Docker Rootless envolve algumas etapas específicas. Primeiro, desinstalamos a versão padrão (se instalada) e configuramos o ambiente de forma adequada. Veja como fazer isso em um sistema Linux moderno:</p>

<pre><code class="language-bash"># 1. Remover Docker padrão (se necessário)

sudo apt-get remove docker docker-engine docker.io containerd runc

2. Instalar dependências necessárias

sudo apt-get install -y uidmap dbus-user-session

3. Baixar e instalar Docker Rootless

curl https://get.docker.com/builds/Linux/x86_64/docker-latest.tgz | tar xz -C /tmp/

/tmp/docker/dockerd-rootless-setuptool.sh install

4. Ativar o serviço para iniciar automaticamente

systemctl --user enable docker

systemctl --user start docker

5. Verificar instalação

dockerd-rootless-setuptool.sh check</code></pre>

<p>Após a instalação, você deve configurar variáveis de ambiente para usar a instância rootless. Adicione ao seu <code>.bashrc</code> ou <code>.zshrc</code>:</p>

<pre><code class="language-bash">export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock</code></pre>

<h3>Validando o Comportamento Rootless</h3>

<p>Uma maneira simples de validar que seu Docker está realmente em modo rootless é executar um container e verificar o UID do processo. Compare o comportamento entre um docker padrão e rootless:</p>

<pre><code class="language-bash"># Em Docker Rootless

docker run --rm ubuntu id

Saída esperada:

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

Mas se você verificar no host:

ps aux | grep &quot;docker run&quot;

O processo estará rodando com UID do usuário não-root</code></pre>

<p>A questão é sutil: dentro do container ele parece ser root (pelo namespace), mas no host real, ele é um usuário normal. Isso oferece proteção significativa contra privilege escalation.</p>

<h2>Capabilities: Controlando Privilégios no Nível do Kernel</h2>

<h3>O que são Capabilities e por que importam</h3>

<p>Linux capabilities dividem os privilégios tradicionais do root em unidades específicas e menores. Ao invés de dar permissão total de root, você pode conceder apenas as capacidades específicas que um processo realmente precisa. Docker, por padrão, mantém um conjunto de capabilities &quot;seguras&quot; e remove outras perigosas.</p>

<p>Um container executando um servidor web nginx, por exemplo, não precisa de <code>CAP_NET_ADMIN</code>, que permite configuração avançada de rede. Não precisa de <code>CAP_SYS_MODULE</code>, que permite carregar módulos do kernel. Ao remover essas capabilities, você reduz drasticamente a superfície de ataque.</p>

<h3>Capabilities Padrão em Docker</h3>

<p>Docker inicia containers com um conjunto padrão de capabilities. Você pode visualizar e modificar esse comportamento. As capabilities padrão incluem permissões básicas para networking e operações de arquivo, mas excluem as mais perigosas:</p>

<pre><code class="language-bash"># Ver capabilities padrão

docker inspect &lt;container_id&gt; | grep -i &quot;cap&quot;

Exemplo de saída esperada:

&quot;CapAdd&quot;: [],

&quot;CapDrop&quot;: [

&quot;NET_RAW&quot;,

&quot;SYS_CHROOT&quot;,

&quot;KILL&quot;,

&quot;SETFCAP&quot;,

&quot;SETPCAP&quot;,

&quot;NET_BIND_SERVICE&quot;,

&quot;SYS_CHROOT&quot;,

&quot;KILL&quot;,

&quot;AUDIT_WRITE&quot;

]</code></pre>

<h3>Removendo e Adicionando Capabilities Seletivamente</h3>

<p>A estratégia &quot;least privilege&quot; significa que você deve começar sem capabilities e adicionar apenas as necessárias. Vamos ver um exemplo prático com um Dockerfile que executa um aplicativo específico:</p>

<pre><code class="language-dockerfile">FROM ubuntu:22.04

RUN apt-get update &amp;&amp; apt-get install -y curl

Criamos um usuário não-root para executar a aplicação

RUN useradd -m -u 1000 appuser

COPY app.sh /home/appuser/

RUN chmod +x /home/appuser/app.sh

USER appuser

ENTRYPOINT [&quot;/home/appuser/app.sh&quot;]</code></pre>

<p>Agora, ao executar esse container, você pode especificar exatamente quais capabilities são necessárias:</p>

<pre><code class="language-bash"># Executar com ALL capabilities removidas (máxima segurança)

docker run --cap-drop=ALL minha-app

Se o app precisa fazer binding em portas &lt; 1024, adicione apenas CAP_NET_BIND_SERVICE

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE minha-app

Para um servidor web que precisa binding em porta 80

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --cap-add=CHOWN minha-app</code></pre>

<h3>Exemplo Prático: Servidor Web Seguro</h3>

<p>Vamos criar um exemplo realista de um container nginx com capabilities otimizadas:</p>

<pre><code class="language-bash"># Executar nginx com apenas as capabilities necessárias

docker run -d \

--name nginx-seguro \

--cap-drop=ALL \

--cap-add=NET_BIND_SERVICE \

--cap-add=CHOWN \

--cap-add=SETUID \

--cap-add=SETGID \

--read-only \

--tmpfs /var/run \

--tmpfs /var/cache/nginx \

-p 80:80 \

nginx:latest</code></pre>

<p>Cada capability tem um propósito específico:</p>

<ul>

<li><code>NET_BIND_SERVICE</code>: permite fazer bind em portas &lt; 1024</li>

<li><code>CHOWN</code>: permite mudar proprietário de arquivos</li>

<li><code>SETUID/SETGID</code>: permite mudar UID/GID de processos</li>

</ul>

<p>Remover capabilities desnecessárias reduz dramaticamente o potencial de dano se a aplicação for comprometida.</p>

<h2>Scanning de Vulnerabilidades em Imagens Docker</h2>

<h3>Por que Scanning é Essencial</h3>

<p>Uma imagem Docker é uma combinação de múltiplas camadas: a imagem base (como Ubuntu ou Alpine), dependências do sistema operacional e dependências de aplicação. Qualquer uma dessas camadas pode conter vulnerabilidades conhecidas (CVEs - Common Vulnerabilities and Exposures). O scanning verifica essas vulnerabilidades antes de colocar a imagem em produção, evitando deploy de código comprometido.</p>

<p>Existem várias ferramentas disponíveis: Trivy (gratuita, open-source e muito usada), Snyk (comercial com plano gratuito), Docker Scout (integrado ao Docker Desktop), e outras. Vamos focar em Trivy por ser a mais acessível e poderosa.</p>

<h3>Instalando e Usando Trivy</h3>

<p>Trivy é uma ferramenta de scanning leve e rápida. Aqui está como instalá-la e usá-la:</p>

<pre><code class="language-bash"># Instalação no Ubuntu/Debian

sudo apt-get install wget apt-transport-https gnupg lsb-release

wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - echo &quot;deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main&quot; | sudo tee -a /etc/apt/sources.list.d/trivy.list

sudo apt-get update

sudo apt-get install trivy

Verificar instalação

trivy version</code></pre>

<h3>Executando Scans Básicos em Imagens</h3>

<p>Depois de instalado, você pode escanear qualquer imagem Docker. O Trivy verifica vulnerabilidades na imagem, analisando o sistema de arquivos e detectando pacotes vulneráveis:</p>

<pre><code class="language-bash"># Scan simples em uma imagem local

trivy image ubuntu:22.04

Scan com saída em formato JSON (útil para integração)

trivy image --format json ubuntu:22.04 &gt; scan-results.json

Scan com saída em tabela resumida

trivy image --severity HIGH,CRITICAL ubuntu:22.04

Scan de uma imagem que ainda não foi baixada

trivy image --download-db-only

trivy image gcr.io/distroless/python3-debian11</code></pre>

<h3>Exemplo Realista: CI/CD com Scanning</h3>

<p>Em um pipeline de CI/CD (usando Docker, Kubernetes ou qualquer orquestrador), você quer falhar o build se vulnerabilidades críticas forem encontradas. Aqui está um exemplo de script que você pode integrar:</p>

<pre><code class="language-bash"></code></pre>

<h3>Integrando Scanning em Docker Compose</h3>

<p>Se você usa Docker Compose, pode adicionar um estágio de scanning antes de colocar imagens em produção:</p>

<pre><code class="language-yaml">version: &#039;3.8&#039;

services:

app:

build:

context: .

dockerfile: Dockerfile

image: minha-app:${VERSION:-latest}

security_opt:

  • no-new-privileges:true

cap-drop:

  • ALL

cap-add:

  • NET_BIND_SERVICE

user: &quot;1000&quot;

read_only: true

tmpfs:

  • /tmp
  • /var/run

security-check:

image: aquasec/trivy:latest

volumes:

  • /var/run/docker.sock:/var/run/docker.sock
  • ./scan-results:/results

command:

  • image
  • --exit-code
  • &quot;1&quot;
  • --severity
  • HIGH,CRITICAL
  • --format
  • json
  • --output
  • /results/scan.json
  • minha-app:${VERSION:-latest}

depends_on:

  • app</code></pre>

<h2>Boas Práticas Integradas</h2>

<h3>Criando um Container Verdadeiramente Seguro</h3>

<p>Combinar todas essas técnicas resulta em um container muito mais seguro. Aqui está um exemplo completo que integra rootless, capabilities limitadas e scanning:</p>

<pre><code class="language-dockerfile">FROM alpine:3.18

Instalar apenas o necessário

RUN apk add --no-cache python3 py3-pip

Criar usuário não-root

RUN addgroup -S appgroup &amp;&amp; adduser -S appuser -G appgroup

WORKDIR /app

Copiar aplicação

COPY app.py .

RUN chown -R appuser:appgroup /app

Rodar como usuário não-root

USER appuser

EXPOSE 8000

Health check

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \

CMD python3 -c &quot;import urllib.request; urllib.request.urlopen(&#039;http://localhost:8000/health&#039;)&quot;

ENTRYPOINT [&quot;python3&quot;, &quot;app.py&quot;]</code></pre>

<p>Para executar esse container com máxima segurança:</p>

<pre><code class="language-bash"># Build

docker build -t minha-app-segura .

Escanear antes de usar

trivy image --severity HIGH,CRITICAL minha-app-segura

Rodar com todas as proteções

docker run \

--name app-segura \

--read-only \

--cap-drop=ALL \

--cap-add=NET_BIND_SERVICE \

--user appuser \

--tmpfs /tmp \

--tmpfs /run \

--security-opt=no-new-privileges:true \

--memory=256m \

--cpus=0.5 \

--pids-limit=100 \

-p 8000:8000 \

minha-app-segura</code></pre>

<h2>Conclusão</h2>

<p>Aprendemos três pilares fundamentais de segurança em Docker que trabalham juntos para proteger seus containers. <strong>Primeiro, Docker Rootless elimina o risco de privilege escalation ao nível do host</strong>, mapeando o usuário root do container para um usuário normal no sistema operacional. <strong>Segundo, capabilities permitem aplicar o princípio de least privilege de forma granular</strong>, concedendo apenas as permissões específicas que cada aplicação realmente necessita. <strong>Terceiro, scanning com ferramentas como Trivy detecta vulnerabilidades conhecidas em imagens antes da produção</strong>, prevenindo deploys de código comprometido. A combinação dessas três estratégias, aplicadas desde o desenvolvimento até a produção, cria uma postura de segurança robusta e defensável.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://docs.docker.com/engine/security/rootless/" target="_blank" rel="noopener noreferrer">Docker Rootless Documentation</a> - Documentação oficial do Docker sobre Rootless</li>

<li><a href="https://man7.org/linux/man-pages/man7/capabilities.7.html" target="_blank" rel="noopener noreferrer">Linux Capabilities Man Page</a> - Referência completa de capabilities do Linux</li>

<li><a href="https://github.com/aquasecurity/trivy" target="_blank" rel="noopener noreferrer">Trivy GitHub Repository</a> - Ferramenta open-source de scanning de vulnerabilidades</li>

<li><a href="https://cheatsheetseries.owasp.org/cheatsheets/Docker_Security_Cheat_Sheet.html" target="_blank" rel="noopener noreferrer">OWASP Docker Security Cheat Sheet</a> - Guia de segurança Docker da OWASP</li>

<li><a href="https://docs.docker.com/engine/security/" target="_blank" rel="noopener noreferrer">Docker Security Best Practices</a> - Documentação oficial de segurança do Docker</li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em DevOps & CI/CD

FinOps e Otimização de Custos em Cloud: Estratégias e Ferramentas na Prática
FinOps e Otimização de Custos em Cloud: Estratégias e Ferramentas na Prática

O que é FinOps e por que você deve se importar FinOps é a prática de trazer d...

Guia Completo de Podman e Alternativas ao Docker: Daemonless Containers na Prática
Guia Completo de Podman e Alternativas ao Docker: Daemonless Containers na Prática

Entendendo Containers Tradicionais vs. Daemonless A história dos containers c...

Boas Práticas de Docker Compose: Ambientes Multi-container e Variáveis de Ambiente para Times Ágeis
Boas Práticas de Docker Compose: Ambientes Multi-container e Variáveis de Ambiente para Times Ágeis

Docker Compose: Ambientes Multi-container e Variáveis de Ambiente Docker Comp...