Posto de Observação

capítulo 04

Exposição & Dashboard

Abrindo o honeypot pra internet de verdade, e montando o painel que mostra o que chega.

Verificar CGNAT antes de tudo

Provedores residenciais (fibra, principalmente) às vezes não entregam IP público real — o cliente fica atrás de um NAT compartilhado da própria operadora (CGNAT). Se for o caso, nenhum port-forward vai funcionar, não importa quão certa esteja a configuração.

curl -4 ifconfig.me

Depois, de um navegador na mesma rede, abra canyouseeme.org, digite a porta que pretende expor e clique em Check. Isso confirma se o forward está alcançável de fora antes de gastar tempo depurando algo que pode ser estruturalmente impossível do seu lado.

nota

Se confirmar CGNAT, port-forward tradicional não resolve — as alternativas são pedir IP público ao provedor, ou usar um túnel reverso (Cloudflare Tunnel, por exemplo), o que muda a arquitetura deste guia.

Configurar o encaminhamento de porta

No painel do seu roteador (nome do menu varia por fabricante: Port Forwarding, Virtual Server, NAT, Encaminhamento de Portas), crie duas regras:

CampoRegra SSHRegra Telnet
Porta externa2223
Porta interna22222223
IP internoIP_DA_VM
ProtocoloTCP

Teste de fora de verdade (dados móveis, não Wi-Fi da mesma rede):

ssh -p 22 root@SEU_IP_PUBLICO

Deve cair no shell falso do Cowrie.

Centralizar logs: Grafana Alloy

O agente que lê os arquivos de log e envia pro banco de dados de logs (Loki) roda nativo na VM — não em container, pelo mesmo motivo do Suricata: precisa de acesso direto ao sistema de arquivos.

sobre a escolha

O agente tradicionalmente usado com o Loki (Promtail) foi descontinuado — o substituto oficial é o Grafana Alloy, que usa uma linguagem de configuração própria (River) em vez de YAML.

sudo mkdir -p /etc/apt/keyrings
sudo wget -qO /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
sudo chmod 644 /etc/apt/keyrings/grafana.asc
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" | sudo tee /etc/apt/sources.list.d/grafana.list

sudo apt update
sudo apt install -y alloy
local.file_match "suricata" {
  path_targets = [{"__path__" = "/var/log/suricata/eve.json", "job" = "suricata"}]
}

local.file_match "cowrie" {
  path_targets = [{"__path__" = "/var/lib/docker/volumes/cowrie-honeypot_cowrie-var/_data/log/cowrie/cowrie.json", "job" = "cowrie"}]
}

loki.source.file "suricata_tail" {
  targets    = local.file_match.suricata.targets
  forward_to = [loki.write.local.receiver]
}

loki.source.file "cowrie_tail" {
  targets    = local.file_match.cowrie.targets
  forward_to = [loki.write.local.receiver]
}

loki.write "local" {
  endpoint {
    url = "http://127.0.0.1:3100/loki/api/v1/push"
  }
}
permissão que quase sempre falta

O Alloy roda como usuário de sistema próprio (alloy), não root. A pasta /var/lib/docker normalmente vem com permissão drwx--x--- — só root e o grupo root atravessam. Isso bloqueia o Alloy de chegar no log do Cowrie, mesmo o arquivo final estando com leitura liberada. Resolva com uma ACL pontual, sem abrir a pasta pra mais ninguém:

sudo setfacl -m u:alloy:x /var/lib/docker
getfacl /var/lib/docker | grep alloy   # confirma: user:alloy:--x

sudo systemctl enable --now alloy
sudo journalctl -u alloy --since "30 seconds ago" --no-pager | grep -i tailing

Devem aparecer duas linhas "start tailing file" — uma pro Suricata, uma pro Cowrie.

Loki + Grafana

Os dois rodam em containers separados, então precisam de uma rede Docker compartilhada pra se enxergarem — 127.0.0.1 de dentro de um container aponta pra ele mesmo, não pro vizinho.

docker network create dashboard-net

mkdir -p ~/honeypot-dashboard && cd ~/honeypot-dashboard
wget https://raw.githubusercontent.com/grafana/loki/v3.7.0/cmd/loki/loki-local-config.yaml -O loki-config.yaml

docker run --name loki -d \
  --network dashboard-net \
  -v $(pwd):/mnt/config \
  -p 127.0.0.1:3100:3100 \
  grafana/loki:3.7.0 -config.file=/mnt/config/loki-config.yaml

docker run --name grafana -d \
  --network dashboard-net \
  -p 127.0.0.1:3000:3000 \
  grafana/grafana:latest

# garanta que sobrevivem a um reboot da VM
docker update --restart unless-stopped loki grafana
nome do container, não ip

Dentro da mesma rede Docker, os containers se resolvem pelo nome via DNS interno — use http://loki:3100 como URL do Data Source no Grafana, não 127.0.0.1.

Acesse o Grafana (que está só em 127.0.0.1) via túnel SSH:

ssh -p 2200 -L 3000:127.0.0.1:3000 usuario@IP_DA_VM

Com o túnel aberto, acesse http://localhost:3000 no navegador da sua máquina.

Montando os painéis

  1. Connections → Data sources → Add data source → Loki, URL http://loki:3100, Save & test.
  2. Dashboards → New → New Dashboard → Add visualization, escolha Loki como fonte.
  3. No editor da query, clique em Code (o modo padrão é visual e não aceita colar a query direto) e cole uma das consultas abaixo.

Tentativas no honeypot ao longo do tempo

Tipo de visualização: Time series.

sum(count_over_time({job="cowrie"}[5m]))

Alertas do Suricata por severidade

Tipo de visualização: Bar chart.

sum by (severity) (count_over_time({job="suricata"} | json | alert_severity != "" [5m]))

Top IPs de origem

Tipo de visualização: Table.

topk(10, sum by (src_ip) (count_over_time({job="cowrie"} | json [1h])))
nota

O painel de severidade provavelmente fica vazio no início — o ruleset padrão do Suricata dispara mais por padrão/contagem (várias tentativas de brute-force num intervalo, por exemplo) do que por conexão isolada. Ele começa a preencher conforme tráfego real de ataque for chegando.