docs(seguranca): documentar incidente daemon Docker root + fix aplicado na VPS 1

- seguranca_deploy_user.md: corrige Pilar 2 — adiciona aviso crítico sobre
  grupo docker em instalações rootless, documenta estado correto da VPS 1 e
  registra incidente de 2026-05-14 com causa raiz e comandos de correção
- scoreodonto/deploy.md: adiciona seção de alerta Docker Duplo com sintoma
  porta 8020 bloqueada por docker-proxy zumbi e diagnóstico/solução
- clube67/arquitetura.md: adiciona regra crítica de Docker Rootless na VPS 1
  com tabela de restrições e verificação de saúde pós-manutenção

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
VPS 4 Builder
2026-05-14 22:34:03 +02:00
parent bcb7c3d856
commit 131d4060fb
3 changed files with 126 additions and 4 deletions
+20
View File
@@ -248,6 +248,26 @@ Para garantir que a invasão de um container não resulte no comprometimento do
* **CAP Drop**: Remove capacidades de sistema desnecessárias ao container (ex: `NET_ADMIN`, `SYS_ADMIN`, `SYS_CHROOT`).
* **Rootless Containers**: Sempre que suportado pela aplicação, os containers devem rodar com IDs de usuário não privilegiados (non-root).
### ⚠️ Regra Crítica — Docker Rootless na VPS 1
A VPS 1 roda **Docker Rootless** (daemon como usuário `deploy`, socket em `/run/user/1000/docker.sock`). O daemon root (`docker.service`) está **desabilitado permanentemente**.
| Regra | Motivo |
|-------|--------|
| `docker.service` deve permanecer `disabled` | Daemon root + `deploy` no grupo `docker` = escalonamento de privilégios trivial |
| `deploy` NÃO deve estar no grupo `docker` | Grupo `docker` dá acesso ao socket root; basta omitir `DOCKER_HOST` para criar containers root |
| Todo script que roda na VPS 1 deve usar `DOCKER_HOST` explícito | SSH não herda variáveis de ambiente do shell do usuário por padrão |
```bash
# Verificação de saúde — executar após qualquer manutenção na VPS 1
sudo systemctl status docker # deve estar: inactive (dead)
groups deploy # não deve conter "docker"
DOCKER_HOST=unix:///run/user/1000/docker.sock docker ps # deve listar os containers ativos
```
> [!WARNING]
> Se o `docker.service` for reativado acidentalmente (ex: `apt upgrade` do pacote docker.io), ele voltará como enabled. Verificar após atualizações de sistema.
---
## 🤖 Fluxo de Auto-Cura (Self-Healing) com Grafana/Gitea
+41
View File
@@ -183,6 +183,47 @@ ssh deploy@10.99.0.1 "docker logs scoreodontocom-scoreodonto-backend-1 --tail 30
| `rsync` falha | `ssh deploy@10.99.0.1 echo ok` | Checar chave SSH e VPN WireGuard |
| Docker build falha | `docker logs` na VPS 1 | Ver logs do container; checar `npm install` |
| Frontend em branco | nginx config / dist vazia | Verificar se `vite build` completou sem erro |
| nginx não sobe, porta 8020 ocupada | `sudo ss -tlnp \| grep 8020` | Ver seção abaixo — docker-proxy zumbi |
---
## 🔒 Alerta de Segurança — Docker Duplo (Rootless + Root)
> [!CAUTION]
> A VPS 1 usa **Docker Rootless**. O daemon root (`docker.service`) está **desabilitado intencionalmente**. Nunca o reative.
### Por que o daemon root é perigoso aqui
O usuário `deploy` tem acesso `sudo`. Se o `docker.service` (root) estiver ativo e `deploy` estiver no grupo `docker`, qualquer `docker compose up` sem `DOCKER_HOST` cria containers como root — paralelos aos rootless, invisíveis ao `docker ps` normal, e inacessíveis ao Traefik (que monitora a rede rootless).
### Sintoma: porta 8020 ocupada após `docker rm -f`
Quando um container nginx é `force-remove`d enquanto o daemon root está ativo, o processo `docker-proxy` root pode ficar preso na porta:
```bash
# Diagnóstico
sudo ss -tlnp | grep 8020
# Se aparecer docker-proxy com PID root → daemon root estava ativo
# Solução de emergência
sudo kill <PID do docker-proxy>
cd ~/stack/scoreodonto.com
DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock docker compose rm -sf nginx
DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock docker compose up -d nginx
```
### Estado correto da VPS 1
```bash
# Verificar que o daemon root está morto
sudo systemctl status docker # deve mostrar: inactive (dead)
# Verificar que deploy não está no grupo docker
groups deploy # não deve conter "docker"
# Verificar que rootless está respondendo
DOCKER_HOST=unix:///run/user/1000/docker.sock docker ps | grep scoreodonto
```
---
+65 -4
View File
@@ -32,16 +32,77 @@ find /var/www/meu-app -type f -exec chmod 644 {} \;
---
## 🐋 Pilar 2: Gerenciamento do Docker sem Root
Se as suas aplicações rodam em Docker (ou Docker Compose), o usuário `deploy` não precisa usar `sudo docker` para gerenciar os containers.
### Prática:
Adicionamos o usuário `deploy` ao grupo do Docker no Linux:
> [!CAUTION]
> **Esta infraestrutura usa Docker Rootless (VPS 1).** As instruções abaixo são válidas para instalações com Docker tradicional. Para Docker Rootless, veja a seção específica mais abaixo.
Se as suas aplicações rodam em Docker (ou Docker Compose) **com o daemon tradicional (root)**, o usuário `deploy` não precisa usar `sudo docker` para gerenciar os containers.
### Prática (Docker tradicional):
Adicionar o usuário `deploy` ao grupo `docker`:
```bash
sudo usermod -aG docker deploy
```
**Resultado**: Após fazer logout e login novamente, o usuário `deploy` poderá rodar comandos como `docker ps`, `docker compose up -d`, `docker restart` e fazer builds de imagens diretamente, com total liberdade, sem nunca usar `sudo`.
**Resultado**: O usuário `deploy` poderá rodar `docker compose up -d`, builds etc. sem `sudo`.
> [!WARNING]
> **Nunca faça isso em servidores que também rodam Docker Rootless.** Adicionar `deploy` ao grupo `docker` dá acesso ao socket `/var/run/docker.sock` (root), criando um vetor de escalonamento de privilégios. Qualquer `docker compose up` sem `DOCKER_HOST` explícito cria containers como root silenciosamente.
---
### Prática correta para Docker Rootless (VPS 1):
A VPS 1 usa **Docker Rootless** (`rootlesskit` + `vpnkit`, daemon rodando como `deploy`). A configuração correta é:
1. **`deploy` NÃO está no grupo `docker`** — sem acesso ao socket root `/var/run/docker.sock`
2. **`DOCKER_HOST` definido por padrão** no `~/.bashrc` e `~/.profile`:
```bash
export DOCKER_HOST=unix:///run/user/1000/docker.sock
```
3. **Daemon root desabilitado** (`docker.service` e `docker.socket` disabled)
Com isso, qualquer `docker` ou `docker compose` executado pelo `deploy` — interativo ou via script SSH — sempre usa o daemon rootless automaticamente.
#### Verificar estado correto:
```bash
# deploy NÃO deve aparecer em:
groups deploy # não deve conter "docker"
# daemon root deve estar morto:
sudo systemctl status docker # inactive (dead)
# rootless deve responder:
DOCKER_HOST=unix:///run/user/1000/docker.sock docker ps
```
---
### Incidente registrado — 2026-05-14
**O que aconteceu:** O daemon root (`/usr/bin/dockerd`) estava ativo em paralelo com o rootless. O usuário `deploy` pertencia ao grupo `docker`. Durante um deploy, `docker compose up --build` executou sem `DOCKER_HOST`, criando containers duplicados como root (frontend, backend, nginx) que rodavam ao lado dos containers rootless corretos — invisíveis ao monitoramento normal (`docker ps` sem sudo não os mostrava).
**Consequência direta:** O `docker-proxy` root reteve a porta 8020 após um `docker rm -f`, bloqueando o nginx rootless de reiniciar. O site ficou inacessível até o `docker-proxy` zumbi ser identificado (`sudo ss -tlnp`) e morto.
**Correção aplicada:**
```bash
# 1. Remover containers e imagens duplicados do daemon root
cd /home/deploy/stack/scoreodonto.com
sudo docker compose down --remove-orphans
sudo docker image rm scoreodontocom-scoreodonto-frontend scoreodontocom-scoreodonto-backend
sudo docker network rm soc web
sudo docker builder prune -af
# 2. Desabilitar daemon root definitivamente
sudo systemctl disable --now docker docker.socket
# 3. Remover deploy do grupo docker
sudo gpasswd -d deploy docker
# 4. Garantir DOCKER_HOST nos shells de login (já estava no .bashrc, adicionado ao .profile)
echo 'export DOCKER_HOST=unix:///run/user/1000/docker.sock' >> ~/.profile
```
---