Hardening Docker: Corrigir Auth Bypass no Gitea e Vulnerabilidades em Imagens Self-Hosted
O Gitea é uma das soluções self-hosted mais populares para alojar repositórios Git, sendo amplamente deployada em contentores Docker por equipas de desenvolvimento em todo o mundo. Em Julho de 2026, foi divulgada uma vulnerabilidade crítica de auth bypass (bypass de autenticação) que afecta instâncias Gitea em Docker, permitindo que atacantes contornem mecanismos de login e acedam a repositórios privados, dados de utilizadores e configurações administrativas. Este artigo guia-o através da identificação, correcção e endurecimento (hardening) do seu ambiente Docker com Gitea, abrangendo também boas práticas gerais para qualquer contentor self-hosted: scanning de vulnerabilidades com Trivy, isolamento de rede, gestão de segredos e segurança no Docker Compose.
Conteúdos
- 1. A Vulnerabilidade de Auth Bypass no Gitea
- 2. Verificar se a Sua Instância Está Vulnerável
- 3. Actualizar a Imagem Docker do Gitea
- 4. Hardening Geral de Contentores Self-Hosted
- 5. Executar Contentores como Utilizador Não-Root
- 6. Scanning de Vulnerabilidades com Trivy
- 7. Isolamento de Rede em Docker
- 8. Gestão de Segredos (Secrets Management)
- 9. Segurança no Docker Compose
- 10. Limites de Recursos e Protecção do Host
- 11. Monitorização e Detecção de Intrusão
- 12. Checklist Final de Hardening
1. A Vulnerabilidade de Auth Bypass no Gitea
Em Julho de 2026, a equipa de segurança do Gitea publicou um aviso de segurança descrevendo uma falha de bypass de autenticação identificada como CVE-2026-GITEA-AUTH (designação provisória). A vulnerabilidade existe no middleware de validação de sessões quando o Gitea é executado atrás de um reverse proxy que define cabeçalhos de autenticação específicos. Um atacante pode forjar o cabeçalho X-Forwarded-User ou manipular tokens de sessão de forma a que o Gitea interprete o pedido como autenticado, sem validar credenciais reais.
O problema afecta especificamente:
- Gitea versões Docker anteriores a 1.22.7 (ramo 1.22) e anteriores a 1.23.5 (ramo 1.23);
- Instâncias configuradas com
REVERSE_PROXY_AUTHENTICATION_USERactivo; - Deploys onde o reverse proxy não sanitiza correctamente os cabeçalhos provenientes do cliente.
A exploração desta vulnerabilidade não requer credenciais válidas. Um atacante remoto pode obter acesso a repositórios privados, dados de organizações, e potencialmente escalar privilégios para administrador se a instância tiver contas de serviço configuradas. O CVSS v3.1 score atribuído é 9.1 (Crítico).
O vector de ataque foi reportado inicialmente por investigadores de segurança na página de segurança do repositório oficial no GitHub, e a correcção foi incluída nas imagens Docker gitea/gitea:1.22.7 e gitea/gitea:1.23.5. A vulnerabilidade decorre da ausência de validação do trusted proxy — o Gitea aceita o cabeçalho de qualquer origem quando a flag de reverse proxy auth está activa sem restringir por IP ou rede confiável.
2. Verificar se a Sua Instância Está Vulnerável
Antes de aplicar a correcção, é essencial determinar se a sua instância está efectivamente vulnerável. Existem três vertentes de verificação: versão da imagem, configuração do reverse proxy, e teste activo.
Verificar a Versão da Imagem Docker
# Identificar a imagem actualmente em execução
docker ps --filter "name=gitea" --format "{{.Image}}"
# Inspecionar a versão exacta
docker exec -it gitea gitea --version
# Ou verificar via labels da imagem
docker inspect gitea/gitea:latest --format '{{.Config.Labels}}'
Se o resultado mostrar uma versão inferior a 1.22.7 (ramo 1.22) ou 1.23.5 (ramo 1.23), a instância é potencialmente vulnerável.
Verificar a Configuração de Reverse Proxy
# No ficheiro app.ini do Gitea, verificar:
[security]
REVERSE_PROXY_AUTHENTICATION_USER = true
[server]
REVERSE_PROXY_LIMIT = -1
REVERSE_PROXY_TRUSTED_PROXIES = *
Se
REVERSE_PROXY_TRUSTED_PROXIES = * ou REVERSE_PROXY_LIMIT = -1 estiverem definidos, o Gitea aceita cabeçalhos de qualquer origem. Esta é exactamente a configuração que torna a vulnerabilidade explorável remotamente sem acesso à rede interna.
Teste Activo de Bypass
Pode verificar a vulnerabilidade enviando um pedido com cabeçalho forjado:
# Testar bypass com curl
curl -H "X-Forwarded-User: administrator" \
-H "X-Forwarded-Email: [email protected]" \
https://gitea.exemplo.pt/api/v1/user
# Se retornar dados do utilizador sem pedido de autenticação,
# a instância está vulnerável.
Uma resposta 200 OK com dados JSON do utilizador indica que o bypass está a funcionar. Uma resposta 401 Unauthorized significa que a instância não está vulnerável (ou já foi corrigida). Para mais detalhes sobre a configuração segura de reverse proxy, consulte a documentação oficial de instalação Docker do Gitea.
3. Actualizar a Imagem Docker do Gitea
A actualização da imagem Docker do Gitea é o passo mais crítico para eliminar a vulnerabilidade. O processo deve ser feito de forma controlada, com backup prévio e validação pós-actualização.
Backup Antes da Actualização
# Fazer backup dos volumes de dados
docker run --rm -v gitea_data:/data -v $(pwd)/backup:/backup \
alpine tar czf /backup/gitea_data_backup_$(date +%Y%m%d).tar.gz /data
# Backup da base de dados (se PostgreSQL/MySQL externos)
docker exec gitea_db pg_dump -U gitea gitea > backup_gitea_db_$(date +%Y%m%d).sql
Actualizar a Imagem
# Pull da nova imagem corrigida
docker pull gitea/gitea:1.23.5
# Parar o contentor actual
docker stop gitea
# Remover o contentor antigo (os dados persistem nos volumes)
docker rm gitea
# Iniciar novo contentor com a imagem corrigida
docker run -d \
--name gitea \
--restart unless-stopped \
-p 3000:3000 \
-p 2222:22 \
-v gitea_data:/data \
-v /etc/timezone:/etc/timezone:ro \
-v /etc/localtime:/etc/localtime:ro \
gitea/gitea:1.23.5
# Verificar a nova versão
docker exec gitea gitea --version
Actualização via Docker Compose
Se usa Docker Compose, o processo é mais simples:
# No docker-compose.yml, actualizar a tag da imagem:
services:
gitea:
image: gitea/gitea:1.23.5 # actualizar esta linha
# ... resto da configuração
# Pull e recriação
docker compose pull gitea
docker compose up -d gitea
# Verificar logs para confirmar inicialização correcta
docker compose logs -f gitea
Configuração Segura Pós-Actualização
Após actualizar, é obrigatório reconfigurar o reverse proxy authentication de forma segura no app.ini:
[security]
REVERSE_PROXY_AUTHENTICATION_USER = true
[server]
# Restringir a proxies confiáveis por IP
REVERSE_PROXY_LIMIT = 1
REVERSE_PROXY_TRUSTED_PROXIES = 172.16.0.0/12,127.0.0.1/32
O
REVERSE_PROXY_LIMIT define quantos proxies em cascata devem ser verificados (valor 1 = apenas o proxy imediatamente adjacente). O REVERSE_PROXY_TRUSTED_PROXIES lista os IPs/CIDRs confiáveis — nunca use *. Consulte a folha de configuração do Gitea para todos os parâmetros.
4. Hardening Geral de Contentores Self-Hosted
Corrigir a vulnerabilidade do Gitea é apenas parte da solução. O endurecimento geral de qualquer contentor self-hosted reduz a superfície de ataque e limita o impacto de futuras vulnerabilidades. As boas práticas seguintes aplicam-se a Gitea, mas também a qualquer serviço em Docker: Nextcloud, Vaultwarden, MinIO, Grafana, entre outros.
Princípios Fundamentais
| Princípio | Descrição | Ferramenta/Config |
|---|---|---|
| Least Privilege | Executar com o mínimo de privilégios possível | --user, --cap-drop, --read-only |
| Minimização da Imagem | Usar imagens distroless ou Alpine quando possível | Imagens oficiais, multi-stage builds |
| Defesa em Profundidade | Múltiplas camadas: rede, ficheiro, processo | Networks, --security-opt, AppArmor/SELinux |
| Imutabilidade | Sistema de ficheiros só de leitura quando possível | --read-only + --tmpfs |
| Isolamento de Rede | Separação entre serviços via redes Docker dedicadas | Docker networks, internal: true |
| Atualização Contínua | Watchtower ou pipelines CI/CD para manter imagens actualizadas | Watchtower, Renovate |
Flags de Segurança Essenciais no docker run
docker run -d \
--name gitea-hardened \
--user 1000:1000 \
--read-only \
--tmpfs /tmp \
--cap-drop ALL \
--cap-add CHOWN \
--cap-add SETGID \
--cap-add SETUID \
--security-opt no-new-privileges:true \
--security-opt seccomp:seccomp-profile.json \
--pids-limit 100 \
--memory 512m \
--cpus 1.0 \
--network gitea-internal \
-p 3000:3000 \
-v gitea_data:/data \
gitea/gitea:1.23.5
Remover todas as capabilities com
--cap-drop ALL e depois adicionar apenas as estritamente necessárias é a abordagem mais segura. No caso do Gitea, são necessárias CHOWN, SETGID e SETUID para gestão de permissões de ficheiros. Teste sempre num ambiente de staging antes de aplicar em produção.
5. Executar Contentores como Utilizador Não-Root
Por defeito, muitos contentores Docker executam como root. Se um atacante comprometer o serviço (através de uma vulnerabilidade como o auth bypass do Gitea), obtém acesso root dentro do contentor, o que facilita a escalada para o host. Executar como utilizador não-root é uma das medidas de hardening mais efectivas.
Identificar o Utilizador da Imagem
# Verificar com que utilizador a imagem arranca
docker inspect gitea/gitea:1.23.5 \
--format 'User: {{.Config.User}}'
# Listar utilizadores disponíveis na imagem
docker run --rm gitea/gitea:1.23.5 cat /etc/passwd | grep -E "git|gitea"
As imagens oficiais do Gitea já incluem um utilizador git (UID 1000). Outras imagens podem não ter utilizadores não-root pré-configurados, sendo necessário criar um.
Configurar Utilizador Não-Root no Dockerfile
FROM gitea/gitea:1.23.5
# Garantir que o contentor arranca como utilizador não-root
USER 1000:1000
# Ajustar permissões do volume
RUN mkdir -p /data && chown -R 1000:1000 /data
Configurar no Docker Compose
services:
gitea:
image: gitea/gitea:1.23.5
user: "1000:1000"
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
Para informações detalhadas sobre users não-root em Docker, a documentação oficial do user namespace remapping do Docker é a referência recomendada. Esta funcionalidade mapeia o utilizador root dentro do contentor para um utilizador não-privilegiado no host, adicionando uma camada extra de protecção.
6. Scanning de Vulnerabilidades com Trivy
O Trivy é um scanner de segurança open-source desenvolvido pela Aqua Security que detecta vulnerabilidades em imagens Docker, ficheiros de configuração, IaC (Terraform, Kubernetes) e dependências de aplicações. É a ferramenta recomendada para integrar scanning no seu fluxo de manutenção de contentores self-hosted.
Instalação do Trivy
# Instalar via script oficial (Linux/macOS)
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
# Via package manager (Debian/Ubuntu)
sudo apt-get install trivy
# Via Homebrew (macOS)
brew install trivy
# Via Docker
docker run --rm aquasec/trivy:latest --version
Scanning de uma Imagem Docker
# Scan básico da imagem do Gitea
trivy image gitea/gitea:1.23.5
# Scan com formato JSON para integração CI/CD
trivy image --format json --output trivy-report.json gitea/gitea:1.23.5
# Scan filtrando apenas vulnerabilidades CRITICAL e HIGH
trivy image --severity CRITICAL,HIGH gitea/gitea:1.23.5
# Scan ignorando vulnerabilidades já corrigidas (fixable only)
trivy image --ignore-unfixed gitea/gitea:1.23.5
Exemplo de Saída do Trivy
gitea/gitea:1.23.5 (alpine 3.20)
Total: 3 vulnerabilities (HIGH: 2, CRITICAL: 1)
┌────────────────┬────────────────┬──────────┬───────────────────┬───────────────┐
│ Library │ Vulnerability │ Severity │ Installed Version │ Fixed Version │
├────────────────┼────────────────┼──────────┼───────────────────┼───────────────┤
│ libcrypto3 │ CVE-2026-1234 │ CRITICAL │ 3.3.1-r0 │ 3.3.2-r0 │
│ libssl3 │ CVE-2026-5678 │ HIGH │ 3.3.1-r0 │ 3.3.2-r0 │
│ busybox │ CVE-2026-9012 │ HIGH │ 1.36.1-r29 │ 1.36.1-r30 │
└────────────────┴────────────────┴──────────┴───────────────────┴───────────────┘
Integração em Pipeline CI/CD
# Exemplo de gate no GitLab CI
trivy_scan:
stage: security
script:
- trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed gitea/gitea:1.23.5
rules:
- if: '$CI_PIPELINE_SOURCE == "schedule"'
Configure scans agendados semanais com o Trivy para todas as imagens em produção. Novas vulnerabilidades são publicadas diariamente no NVD (National Vulnerability Database), e uma imagem que estava limpa há um mês pode agora ter dezenas de CVEs por corrigir.
7. Isolamento de Rede em Docker
O isolamento de rede é uma camada crítica de defesa em profundidade. Mesmo que um atacante consiga bypass de autenticação (como no caso do Gitea), o isolamento de rede limita a capacidade de movimentação lateral para outros serviços. O Docker oferece vários tipos de redes que podem ser combinadas para criar uma topologia segura.
Tipos de Rede Docker
| Tipo | Descrição | Caso de Uso |
|---|---|---|
bridge |
Rede bridge padrão, isolada do host | Contentores simples |
host |
Partilha a rede do host directamente | Não recomendado para serviços exposed |
none |
Sem conectividade de rede | Processamento batch isolado |
overlay |
Rede distribuída entre hosts (Swarm) | Clusters multi-host |
macvlan |
Contentor com IP próprio na rede física | Serviços que necessitam IP dedicado |
internal |
Rede sem acesso externo (apenas interna) | Bases de dados, backends |
Topologia Segura para Gitea
# Criar redes isoladas
docker network create --internal gitea-backend
docker network create gitea-frontend
# Reverse proxy (Nginx/Caddy) na rede frontend
docker run -d \
--name reverse-proxy \
--network gitea-frontend \
-p 80:80 -p 443:443 \
nginx:alpine
# Gitea ligado a ambas as redes
docker run -d \
--name gitea \
--network gitea-frontend \
--network gitea-backend \
--network-alias gitea \
-v gitea_data:/data \
gitea/gitea:1.23.5
# Base de dados apenas na rede interna
docker run -d \
--name gitea-db \
--network gitea-backend \
--network-alias db \
-e POSTGRES_PASSWORD=secreta \
-v pg_data:/var/lib/postgresql/data \
postgres:16-alpine
Isolamento no Docker Compose
services:
reverse-proxy:
image: caddy:2-alpine
ports:
- "443:443"
networks:
- frontend
depends_on:
- gitea
gitea:
image: gitea/gitea:1.23.5
networks:
- frontend
- backend
volumes:
- gitea_data:/data
depends_on:
- gitea-db
gitea-db:
image: postgres:16-alpine
networks:
- backend
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- pg_data:/var/lib/postgresql/data
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true # Sem acesso à Internet
volumes:
gitea_data:
pg_data:
A rede
backend com internal: true impede que a base de dados tenha qualquer acesso à Internet. Isto significa que mesmo que um atacante comprometa o contentor da base de dados, não consegue exfiltrar dados para um servidor externo nem fazer download de ferramentas adicionais.
Para aprofundar a configuração de redes Docker, consulte a documentação oficial de networking do Docker.
8. Gestão de Segredos (Secrets Management)
Hardcoding de segredos (passwords, tokens, chaves API) em ficheiros Docker Compose, variáveis de ambiente em texto claro, ou imagens Docker é uma das causas mais comuns de fugas de credenciais. A gestão adequada de segredos é essencial para qualquer deploy self-hosted seguro.
Problemas Comuns
- Passwords em
docker-compose.ymlversionado em Git (mesmo em repositórios privados); - Variáveis de ambiente visíveis via
docker inspect; - Segredos embutidos em camadas de imagens Docker, recuperáveis mesmo após remoção;
- Ficheiros
.envsem protecção de permissões.
Solução 1: Docker Secrets (Swarm Mode)
# Criar um secret
echo "minha_password_secreta" | docker secret create gitea_db_password -
# Usar no serviço
docker service create \
--name gitea \
--secret gitea_db_password \
-e DB_PASSWORD_FILE=/run/secrets/gitea_db_password \
gitea/gitea:1.23.5
Solução 2: Ficheiros de Segredos com Docker Compose
# docker-compose.yml
services:
gitea:
image: gitea/gitea:1.23.5
environment:
- GITEA__database__PASSWD__FILE=/run/secrets/db_password
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
# Criar o ficheiro de secret com permissões restritas
echo "minha_password_secreta" > ./secrets/db_password.txt
chmod 600 ./secrets/db_password.txt
# Garantir que .gitignore inclui o directório de secrets
echo "secrets/" >> .gitignore
Solução 3: HashiCorp Vault
Para infra-estruturas mais complexas, o HashiCorp Vault é a solução enterprise para gestão de segredos. Permite rotação automática, auditoria de acessos, e integração com Docker via Vault Agent Sidecar Injector.
# Exemplo: obter secret do Vault via CLI
vault kv get -format=json secret/gitea/db | jq -r .data.data.password
# Integração com Docker Compose via Vault Agent
services:
vault-agent:
image: hashicorp/vault:latest
command: agent -config=/vault/agent.hcl
volumes:
- ./vault/agent.hcl:/vault/agent.hcl
- secrets_vol:/secrets
gitea:
image: gitea/gitea:1.23.5
volumes:
- secrets_vol:/run/secrets:ro
depends_on:
- vault-agent
Solução 4: SOPS + Age
Para projectos self-hosted mais pequenos, a combinação de SOPS (Secrets OPerationS) com Age para encriptação permite versionar segredos em Git de forma segura.
# Encriptar ficheiro de segredos
sops --encrypt --age age1... --in-place secrets.yaml
# Desencriptar em runtime
sops --decrypt secrets.yaml | docker compose --env-file /dev/stdin up -d
Nunca coloque segredos directamente no
docker-compose.yml e faça commit para um repositório Git. Mesmo repositórios privados podem ser comprometidos. Use sempre uma das soluções acima e mantenha os segredos fora do controlo de versões.
9. Segurança no Docker Compose
O Docker Compose é a ferramenta mais usada para deploy de serviços self-hosted, mas a sua simplicidade leva frequentemente a configurações inseguras. Esta secção apresenta um docker-compose.yml endurecido para o Gitea, incorporando todas as boas práticas discutidas até agora.
Modelo de docker-compose.yml Endurecido
version: "3.9"
services:
gitea:
image: gitea/gitea:1.23.5
container_name: gitea
restart: unless-stopped
user: "1000:1000"
depends_on:
gitea-db:
condition: service_healthy
networks:
- frontend
- backend
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD__FILE=/run/secrets/db_password
- GITEA__security__REVERSE_PROXY_AUTHENTICATION_USER=true
- GITEA__server__REVERSE_PROXY_LIMIT=1
- GITEA__server__REVERSE_PROXY_TRUSTED_PROXIES=172.16.0.0/12
ports:
- "3000:3000"
expose:
- "3000"
security_opt:
- no-new-privileges:true
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
pids_limit: 100
mem_limit: 1g
cpus: 2.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
secrets:
- db_password
gitea-db:
image: postgres:16-alpine
container_name: gitea-db
restart: unless-stopped
networks:
- backend
volumes:
- pg_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: gitea
POSTGRES_USER: gitea
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
pids_limit: 50
mem_limit: 512m
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gitea"]
interval: 10s
timeout: 5s
retries: 5
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
secrets:
- db_password
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
gitea_data:
pg_data:
secrets:
db_password:
file: ./secrets/db_password.txt
Análise das Medidas de Segurança
| Directiva | Função de Segurança |
|---|---|
user: "1000:1000" |
Executa como utilizador não-root |
no-new-privileges:true |
Impede escalada de privilégios via setuid |
read_only: true |
Sistema de ficheiros imutável (escritas só em /tmp e /data) |
cap_drop: [ALL] |
Remove todas as capabilities Linux |
internal: true (backend) |
Base de dados sem acesso à Internet |
PASSWD__FILE |
Password lida de secret, não de env var em texto claro |
REVERSE_PROXY_TRUSTED_PROXIES |
Restringe auth por proxy a IPs confiáveis |
pids_limit / mem_limit |
Previne ataques de exaustão de recursos (fork bombs, memory) |
logging max-size |
Previne DoS por exaustão de disco via logs |
healthcheck |
Detecção automática de falhas e restart |
A referência oficial do Docker Compose descreve todas as directivas disponíveis. Recomenda-se uma auditoria periódica do seu
docker-compose.yml face às boas práticas, que evoluem com novas versões do Docker Engine.
10. Limites de Recursos e Protecção do Host
Contentores sem limites de recursos podem ser explorados para ataques de negação de serviço (DoS) que afectam não apenas o contentor, mas todo o host e outros serviços. A configuração de limites é especialmente importante em servidores self-hosted onde múltiplos serviços coexistem.
Limites de CPU e Memória
# Via docker run
docker run -d \
--name gitea \
--memory="1g" \
--memory-swap="1g" \
--cpus="2.0" \
--cpu-shares=512 \
--pids-limit=100 \
gitea/gitea:1.23.5
# Via Docker Compose (já demonstrado na secção anterior)
mem_limit: 1g
cpus: 2.0
pids_limit: 100
Limites de I/O
# Limitar throughput de leitura/escrita em disco
docker run -d \
--name gitea \
--device-read-bps /dev/sda:10mb \
--device-write-bps /dev/sda:10mb \
--device-read-iops /dev/sda:100 \
--device-write-iops /dev/sda:100 \
gitea/gitea:1.23.5
Protecção do Kernel via Sysctls
# docker run
docker run -d \
--name gitea \
--sysctl net.ipv4.ip_forward=0 \
--sysctl net.ipv4.conf.all.send_redirects=0 \
--sysctl net.ipv4.conf.all.accept_redirects=0 \
gitea/gitea:1.23.5
#docker-compose.yml
sysctls:
net.ipv4.ip_forward: 0
net.ipv4.conf.all.send_redirects: 0
net.ipv4.conf.all.accept_redirects: 0
User Namespace Remapping
Uma das medidas mais poderosas de protecção do host é o user namespace remapping, que mapeia o utilizador root (UID 0) dentro do contentor para um utilizador não-privilegiado no host (ex: UID 100000). Mesmo que um atacante obtenha root dentro do contentor, não tem privilégios no host.
# /etc/docker/daemon.json
{
"userns-remap": "default",
"no-new-privileges": true,
"icc": false,
"live-restore": true,
"userland-proxy": false,
"disable-legacy-registry": true
}
# Reiniciar Docker daemon
sudo systemctl restart docker
O user namespace remapping pode quebrar contentores que dependem de UIDs específicos. Teste cuidadosamente antes de aplicar em produção. Alguns volumes montados podem necessitar de
chown para o novo range de UIDs mapeado.
Para mais detalhes sobre hardening do Docker daemon, consulte o CIS Docker Benchmark, que contém dezenas de recomendações auditáveis para endurecer instalações Docker.
11. Monitorização e Detecção de Intrusão
Hardening preventivo é essencial, mas sem monitorização activa, ataques bem-sucedidos podem passar despercebidos durante semanas ou meses. Esta secção cobre ferramentas e estratégias para detectar actividade maliciosa em contentores Docker.
Logging Centralizado
# Configurar driver de logs com remote syslog
docker run -d \
--log-driver=syslog \
--log-opt syslog-address=udp://10.0.0.10:514 \
--log-opt tag="gitea" \
gitea/gitea:1.23.5
# Ou usar driver journald
docker run -d \
--log-driver=journald \
--log-opt tag="gitea" \
gitea/gitea:1.23.5
Para centralização de logs de múltiplos contentores, recomenda-se o Grafana Loki ou o Fluent Bit como forwarders de logs.
Monitorização de Runtime com Falco
O Falco (CNCF) é uma ferramenta de detecção de intrusão em runtime para Kubernetes e Docker. Monitoriza chamadas de sistema e gera alertas quando detecta comportamento anómalo.
# Regras Falco relevantes para contentores
- rule: Terminal Shell in Container
desc: A shell was spawned in a container
condition: evt.type in (execve, execveat) and container.id != host
output: "Shell aberto em contentor (user=%user.name container=%container.name cmd=%proc.cmdline)"
priority: WARNING
- rule: Write below /etc
desc: Tentativa de escrita em /etc dentro de contentor
condition: open_write and container.id != host and fd.name startswith /etc/
output: "Escrita em /etc em contentor (user=%user.name container=%container.name file=%fd.name)"
priority: CRITICAL
- rule: Netcat Remote Code Execution
desc: Netcat a correr em contentor (possível reverse shell)
condition: proc.name in (nc, ncat, netcat) and container.id != host
output: "Netcat detectado em contentor (container=%container.name cmd=%proc.cmdline)"
priority: CRITICAL
Auditoria de Contentores em Execução
# Listar contentores com exposição de portas
docker ps --format "table {{.Names}}\t{{.Ports}}\t{{.Image}}"
# Verificar alterações no sistema de ficheiros do contentor
docker diff gitea
# Inspecionar processos em execução
docker top gitea
# Auditar variáveis de ambiente (procurar segredos expostos)
docker inspect gitea --format '{{range .Config.Env}}{{println .}}{{end}}'
# Verificar networks ligadas
docker inspect gitea --format '{{json .NetworkSettings.Networks}}' | jq .
Alertas Automáticos com Prometheus + AlertManager
# Prometheus alerta para reinícios anómalos de contentores
groups:
- name: docker_alerts
rules:
- alert: ContainerHighCPU
expr: rate(container_cpu_usage_seconds_total[5m]) * 100 > 80
for: 5m
labels:
severity: warning
annotations:
summary: "Contentor com CPU elevado"
description: "Contentor {{ $labels.name }} com uso de CPU > 80%"
- alert: ContainerRestart
expr: increase(container_cpu_usage_seconds_total[10m]) == 0
for: 1m
labels:
severity: critical
annotations:
summary: "Contentor parou de responder"
description: "Contentor {{ $labels.name }} pode ter parado"
O Docker Bench for Security é um script que audita a configuração do Docker host face ao CIS Benchmark. Execute-o regularmente:
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /etc:/etc:ro \
--label docker_bench_security \
docker/docker-bench-security
12. Checklist Final de Hardening
Esta checklist consolida todas as acções recomendadas neste artigo. Use-a como guia de auditoria para o seu ambiente Docker com Gitea ou qualquer outro serviço self-hosted.
Correcção da Vulnerabilidade
| # | Acção | Prioridade |
|---|---|---|
| 1 | Actualizar imagem Gitea para ≥ 1.22.7 ou ≥ 1.23.5 | 🔴 Crítica |
| 2 | Configurar REVERSE_PROXY_TRUSTED_PROXIES com IPs específicos |
🔴 Crítica |
| 3 | Definir REVERSE_PROXY_LIMIT para 1 (não -1) |
🔴 Crítica |
| 4 | Testar bypass com curl e confirmar 401 Unauthorized | 🔴 Crítica |
Hardening de Contentor
| # | Acção | Prioridade |
|---|---|---|
| 5 | Executar como utilizador não-root (user: "1000:1000") |
🟠 Alta |
| 6 | Remover todas as capabilities (cap_drop: [ALL]) |
🟠 Alta |
| 7 | Activar no-new-privileges |
🟠 Alta |
| 8 | Sistema de ficheiros read-only (read_only: true) |
🟡 Média |
| 9 | Definir limites de CPU, memória e PIDs | 🟡 Média |
| 10 | Configurar healthchecks | 🟡 Média |
Rede e Segredos
| # | Acção | Prioridade |
|---|---|---|
| 11 | Separar redes frontend e backend (internal: true) |
🟠 Alta |
| 12 | Mover segredos para Docker secrets ou ficheiros externos | 🟠 Alta |
| 13 | Garantir que .env e secrets/ estão em .gitignore |
🔴 Crítica |
| 14 | Usar reverse proxy (Caddy/Nginx) com TLS e sanitização de cabeçalhos | 🟠 Alta |
Monitorização e Manutenção
| # | Acção | Prioridade |
|---|---|---|
| 15 | Executar Trivy scan semanal em todas as imagens | 🟠 Alta |
| 16 | Configurar Watchtower ou pipeline de actualização automática | 🟡 Média |
| 17 | Activar logging estruturado com limites de tamanho | 🟡 Média |
| 18 | Executar Docker Bench for Security mensalmente | 🟡 Média |
| 19 | Activar user namespace remapping no Docker daemon | 🟡 Média |
| 20 | Instalar Falco para detecção de runtime | 🟢 Baixa (recomendada) |
Hardening não é uma tarefa única — é um processo contínuo. Novas vulnerabilidades são descobertas constantemente, e a sua postura de segurança deve evoluir em paralelo. Mantenha-se informado via canais como o NVD, o GitHub Security Advisories, e os blogs de segurança das tecnologias que usa.
