Plan2026/04-decision-logs.md

503 lines
23 KiB
Markdown
Raw Permalink Normal View History

2026-03-14 19:32:30 +00:00
# Decision: Logs centralizados (punto 1.4)
## Por que es critico
En este incidente, el backdoor capturo credenciales durante 4 dias en `/var/log/ssh_auth.log`. Ese fichero estaba **solo en el servidor comprometido**. Si el atacante lo hubiera borrado (trivial con acceso root), no habria quedado evidencia de que contraseñas fueron robadas ni desde cuando.
Los logs locales en un servidor comprometido no son fiables. Necesitamos que los logs salgan del servidor **en tiempo real** a un sitio que el atacante no pueda tocar.
---
## Que logs centralizar (minimo viable)
### Prioridad 1 - Seguridad (dia 1)
| Log | Que contiene | Por que es critico |
|-----|-------------|-------------------|
| `auth.log` | Logins SSH, sudo, fallos de autenticacion, PAM events | Detecta accesos no autorizados, brute force, uso de credenciales robadas |
| `syslog` | Eventos de sistema, servicios que arrancan/paran, errores kernel | Detecta cambios de configuracion, servicios sospechosos, fallos |
| Firewall PVE (`pvefw-logger`) | Conexiones bloqueadas y permitidas por regla | Detecta intentos de conexion al C2, escaneos, trafico anormal |
### Prioridad 2 - Operaciones (semana 1-2)
| Log | Que contiene | Por que es critico |
|-----|-------------|-------------------|
| PBS task logs | Resultado de GC, verify, sync jobs | Detecta jobs fallidos antes de que el exporter reporte |
| PBS API log | Peticiones a la API de PBS | Detecta accesos sospechosos a datastores, intentos de borrado |
| ZFS events (`zed`) | Scrub results, errores de disco, pool events | Detecta degradacion de hardware antes de que falle |
### Prioridad 3 - Infraestructura (mes 1)
| Log | Que contiene | Por que es critico |
|-----|-------------|-------------------|
| Docker logs (VMs) | Stdout/stderr de contenedores | Errores de PowerDNS, Traefik, MariaDB |
| dpkg.log | Paquetes instalados/actualizados/eliminados | Detecta instalacion de software no autorizado |
| cron logs | Ejecucion de cron jobs | Detecta cron jobs añadidos o modificados |
| Proxmox cluster logs | Eventos del cluster PVE | Problemas de quorum, migraciones, fallos |
---
## Opciones evaluadas
### Opcion A: Grafana Loki + Alloy (RECOMENDADA)
#### Que es
Loki es un sistema de logs diseñado por Grafana Labs. No indexa el contenido de los logs (como Elasticsearch), sino solo las etiquetas (labels). Esto lo hace **mucho mas ligero** en CPU, RAM y disco.
Alloy (antes Promtail + Grafana Agent) es el recolector que envia logs a Loki.
#### Por que encaja
Ya hemos elegido VictoriaMetrics + Grafana para metricas. Añadir Loki significa:
- **Mismo Grafana** para metricas Y logs (una sola UI)
- Correlacion directa: ver un pico de CPU en Grafana y saltar a los logs de ese servidor en el mismo momento
- LogQL (lenguaje de consulta) es similar a PromQL
- Misma filosofia de labels que Prometheus/VictoriaMetrics
#### Arquitectura
```
Servidores PBS/PVE VM Monitoring
┌──────────────────┐ ┌─────────────────────┐
│ Grafana Alloy │──push logs──> │ Loki │
│ (en cada server) │ │ (almacen de logs) │
│ │ │ │
│ Lee: │ │ Grafana │
│ - /var/log/auth │ │ (consulta Loki + │
│ - /var/log/syslog│ │ VictoriaMetrics) │
│ - journald │ │ │
│ - PBS logs │ │ VictoriaMetrics │
│ - PVE firewall │ │ (metricas) │
└──────────────────┘ └─────────────────────┘
```
#### Alloy vs Promtail
Promtail era el recolector original de Loki. Grafana lo ha reemplazado por **Alloy** (un agente unificado que hace log shipping, metricas, y traces):
| Aspecto | Promtail (legacy) | Alloy (actual) |
|---------|-------------------|----------------|
| Estado | En mantenimiento, no nuevas features | Desarrollo activo |
| Funcion | Solo logs | Logs + metricas + traces |
| Config | YAML propio | River (nuevo formato, mas potente) |
| Puede reemplazar a | Solo Promtail | Promtail + node_exporter + vmagent |
**Recomendacion**: Si desplegais Alloy para logs, podeis usarlo tambien como recolector de metricas (reemplaza node_exporter + vmagent en cada servidor). Un solo agente para todo. Pero esto añade complejidad inicial; se puede empezar con Alloy solo para logs y expandir despues.
#### Dimensionamiento Loki
Para 100-500 servidores:
| Parametro | 100 servers | 500 servers |
|-----------|-------------|-------------|
| Ingestion estimada | ~5GB/dia | ~25GB/dia |
| Almacenamiento (30 dias) | ~50GB comprimido | ~250GB comprimido |
| RAM Loki | 2-4 GB | 4-8 GB |
| CPU Loki | 2 vCPU | 4 vCPU |
| RAM Alloy por servidor | 30-50 MB | 30-50 MB |
Loki comprime los logs agresivamente (5-10x). 30 dias de 500 servidores caben en ~250GB.
**Despliegue**: single-node mode (monolitico). Suficiente hasta ~1000 servidores. Mas alla, se escala a microservices mode.
#### Almacenamiento backend
Loki puede guardar los logs en:
- **Filesystem local** (simple, para empezar)
- **S3/Minio** (recomendado para produccion, separacion compute/storage)
- **GCS/Azure Blob** (si usais cloud)
**Recomendacion para empezar**: filesystem local en la VM de monitoring. Migrar a Minio/S3 cuando necesiteis mas retencion o redundancia.
#### Configuracion Alloy en cada servidor
```river
// /etc/alloy/config.alloy
// Recoleccion de logs para envio a Loki
// === FUENTE: Journal (systemd) ===
loki.source.journal "system" {
forward_to = [loki.write.default.receiver]
labels = {
job = "systemd",
host = env("HOSTNAME"),
component = "journal",
}
// Solo unidades relevantes
matches = "_TRANSPORT=syslog OR _TRANSPORT=kernel OR _SYSTEMD_UNIT=sshd.service OR _SYSTEMD_UNIT=proxmox-backup-proxy.service OR _SYSTEMD_UNIT=proxmox-backup.service OR _SYSTEMD_UNIT=pvedaemon.service OR _SYSTEMD_UNIT=pveproxy.service OR _SYSTEMD_UNIT=pve-firewall.service"
}
// === FUENTE: auth.log (critico para seguridad) ===
local.file_match "auth" {
path_targets = [{"__path__" = "/var/log/auth.log"}]
}
loki.source.file "auth" {
targets = local.file_match.auth.targets
forward_to = [loki.write.default.receiver]
labels = {
job = "auth",
host = env("HOSTNAME"),
}
}
// === FUENTE: syslog ===
local.file_match "syslog" {
path_targets = [{"__path__" = "/var/log/syslog"}]
}
loki.source.file "syslog" {
targets = local.file_match.syslog.targets
forward_to = [loki.write.default.receiver]
labels = {
job = "syslog",
host = env("HOSTNAME"),
}
}
// === FUENTE: PVE firewall ===
local.file_match "pvefw" {
path_targets = [{"__path__" = "/var/log/pve-firewall.log"}]
}
loki.source.file "pvefw" {
targets = local.file_match.pvefw.targets
forward_to = [loki.write.default.receiver]
labels = {
job = "pve-firewall",
host = env("HOSTNAME"),
}
}
// === FUENTE: dpkg (instalacion de paquetes) ===
local.file_match "dpkg" {
path_targets = [{"__path__" = "/var/log/dpkg.log"}]
}
loki.source.file "dpkg" {
targets = local.file_match.dpkg.targets
forward_to = [loki.write.default.receiver]
labels = {
job = "dpkg",
host = env("HOSTNAME"),
}
}
// === DESTINO: Loki central ===
loki.write "default" {
endpoint {
url = "http://loki.monitoring.internal:3100/loki/api/v1/push"
}
}
```
#### Configuracion Loki (single-node)
```yaml
# /etc/loki/loki.yaml
auth_enabled: false
server:
http_listen_port: 3100
log_level: warn
common:
path_prefix: /data/loki
storage:
filesystem:
chunks_directory: /data/loki/chunks
rules_directory: /data/loki/rules
replication_factor: 1
ring:
kvstore:
store: inmemory
schema_config:
configs:
- from: 2026-01-01
store: tsdb
object_store: filesystem
schema: v13
index:
prefix: index_
period: 24h
limits_config:
retention_period: 30d # 30 dias de retencion
max_query_series: 1000
ingestion_rate_mb: 20 # 20MB/s ingestion (sobra para 500 servers)
ingestion_burst_size_mb: 40
compactor:
working_directory: /data/loki/compactor
retention_enabled: true
delete_request_cancel_period: 24h
```
#### Consultas utiles en LogQL (desde Grafana)
```logql
# Logins SSH exitosos (quien entro y desde donde)
{job="auth", host="ns31787946"} |= "Accepted"
# Intentos de login fallidos
{job="auth"} |= "Failed password" | pattern `Failed password for <user> from <ip>`
# Alguien instalo un paquete
{job="dpkg"} |= "install" | pattern `<_> install <package> <_> <version>`
# Firewall: conexiones bloqueadas al C2
{job="pve-firewall"} |= "DROP" |= "91.208.162"
# Errores en PBS
{job="systemd", host=~"pbs.*"} |= "error" | logfmt
# Todo lo que paso en un servidor en los ultimos 30 minutos
{host="ns31787946"} | line_format "{{.job}}: {{__line__}}"
# Top 10 IPs con mas intentos fallidos de SSH
{job="auth"} |= "Failed password" | pattern `from <ip> port` | count by (ip) | topk(10, count)
```
#### Alertas en Loki (via Grafana alerting o Loki ruler)
```yaml
# Alerta: mas de 10 intentos fallidos de SSH en 5 minutos
- alert: SSHBruteForce
expr: |
count_over_time({job="auth"} |= "Failed password" [5m]) > 10
for: 1m
labels:
severity: warning
# Alerta: login SSH desde IP no conocida
- alert: SSHUnknownIP
expr: |
count_over_time({job="auth"} |= "Accepted" |~ "(?!83\\.50\\.21|213\\.32\\.37|178\\.33\\.167|37\\.133\\.200)" [5m]) > 0
for: 0m
labels:
severity: critical
# Alerta: paquete instalado fuera de ventana de mantenimiento
- alert: UnexpectedPackageInstall
expr: |
count_over_time({job="dpkg"} |= "install" [5m]) > 0
for: 0m
labels:
severity: warning
# Alerta: alguien modifico PAM (habria pillado el backdoor)
- alert: PAMModification
expr: |
count_over_time({job="syslog"} |= "pam_exec" [5m]) > 0
for: 0m
labels:
severity: critical
```
---
### Opcion B: Graylog
#### Que es
Plataforma de gestion de logs con busqueda, alertas, dashboards, y pipelines de procesamiento. Usa Elasticsearch/OpenSearch como backend.
#### Ventajas sobre Loki
- **Busqueda full-text indexada**: buscar en el contenido de los logs es instantaneo (Loki es mas lento en busquedas sin label)
- **Pipelines de procesamiento**: parsear, enriquecer, transformar logs antes de almacenar
- **UI de logs dedicada**: pensada exclusivamente para analisis de logs (Grafana es mas generica)
- **Alertas de log nativas**: sin necesitar Grafana
- **Extractor de campos**: parsea syslog, JSON, etc. automaticamente
#### Desventajas
- **Requiere Elasticsearch/OpenSearch + MongoDB**: 3 componentes pesados
- **Consumo de recursos**: minimo 4GB RAM solo para Elasticsearch, 2GB para Graylog, 1GB para MongoDB = **7GB minimo**
- **Otro UI**: metricas en Grafana, logs en Graylog. Dos sitios diferentes para investigar un problema
- **Mas complejo de operar**: Elasticsearch necesita cuidado (shards, indices, GC de Java)
- **No correlacion directa** metricas-logs en una sola vista
#### Veredicto
**Potente pero pesado.** Si el equipo ya conoce Graylog/ELK, es buena opcion. Si empezais de cero, el overhead operacional y de recursos no se justifica cuando Loki + Grafana cubre el 90% de las necesidades con un tercio de los recursos.
---
### Opcion C: ELK Stack (Elasticsearch + Logstash + Kibana)
#### Veredicto rapido
**Descartado para vuestro caso.** ELK es el estandar de la industria para logs a gran escala, pero:
- Elasticsearch consume minimo 8-16GB RAM para ser usable
- Kibana es otra UI separada de Grafana
- Logstash es Java y consume mucho
- A 100-500 servidores, es matar moscas a cañonazos
- El coste operacional de mantener un cluster Elasticsearch no compensa
Si algun dia superais los 5000 servidores o necesitais cumplir regulaciones que exigen SIEM certificado, evaluarlo. Para ahora, no.
---
## Comparativa final
| Aspecto | A: Loki + Alloy (RECOMENDADA) | B: Graylog | C: ELK |
|---------|-------------------------------|-----------|--------|
| RAM backend | 2-4 GB (100 servers) | 7+ GB | 16+ GB |
| Disco (30 dias, 100 servers) | ~50 GB | ~200 GB | ~300 GB |
| Complejidad operacional | Baja | Media-alta | Alta |
| UI | Grafana (ya la tenemos) | Propia (separada) | Kibana (separada) |
| Correlacion metricas-logs | Nativa en Grafana | No directa | No directa |
| Busqueda full-text | Lenta sin labels | Rapida (indexada) | Rapida (indexada) |
| Parseo de logs | Basico (LogQL patterns) | Potente (pipelines) | Muy potente (Logstash) |
| Alertas sobre logs | Grafana alerting / Loki ruler | Nativas | ElastAlert / Kibana |
| Agente | Alloy (30-50MB RAM) | Sidecar/Filebeat | Filebeat (similar) |
| Ecosistema Grafana | Nativo | Plugin | Plugin |
---
## Recomendacion: Opcion A (Loki + Alloy)
### Razon principal
Ya tenemos Grafana y VictoriaMetrics. Loki se integra nativamente. Un solo panel para metricas + logs + alertas. El equipo aprende una sola herramienta.
### Despliegue en fases
**Fase 1 (dia 1-2): Seguridad minima**
1. Desplegar Loki single-node en la VM de monitoring (junto a VictoriaMetrics y Grafana)
2. Desplegar Alloy en todos los PBS/PVE via Ansible
3. Recolectar solo: `auth.log` + `syslog`
4. Configurar alerta de "login desde IP no conocida"
5. Verificar que los logs llegan consultando en Grafana
**Fase 2 (semana 1): Operaciones**
1. Añadir fuentes: firewall PVE, dpkg.log, PBS logs (journald)
2. Alertas: brute force SSH, instalacion de paquetes, errores PBS
3. Dashboard de logs de seguridad en Grafana
**Fase 3 (semana 2-3): Visibilidad completa**
1. Añadir Docker logs desde VMs (PowerDNS, Traefik, etc.)
2. Labels enriquecidos: tipo de servidor (pbs/pve/dns), cliente, datacenter
3. Dashboard correlacionado: metricas + logs en la misma vista temporal
**Fase 4 (mes 2): Retencion y backup**
1. Evaluar migracion de filesystem a Minio/S3 para retencion larga
2. Backup de Loki data
3. Retencion diferenciada: auth.log 90 dias, syslog 30 dias, dpkg 180 dias
---
## Diagrama del stack completo (Capa 1: Visibilidad + Capa 2: Seguridad)
```
┌─────────────────────────────────────┐
│ AUTHENTIK │
│ SSO + MFA centralizado │
│ (todos los paneles web se │
│ autentican contra Authentik) │
└──────────────────┬──────────────────┘
│ SSO
┌────────────────────────────┼────────────────────────────┐
│ │ │
┌─────────▼─────────┐ ┌─────────────▼──────────┐ ┌────────────▼───────────┐
│ GRAFANA │ │ Wazuh Dashboard │ │ Forgejo / Semaphore │
│ (UI unificada) │ │ (seguridad) │ │ Netbox / Vaultwarden │
│ Metricas + Logs │ │ FIM, rootkits, vulns │ │ (todos con SSO) │
│ Alertas→Telegram │ │ Alertas→Telegram │ │ │
└───┬──────────┬────┘ └────────────┬───────────┘ └────────────────────────┘
│ │ │
┌───▼──────┐ ┌▼───────────┐ ┌──────▼───────────┐
│Victoria │ │ Loki │ │ Wazuh Manager │
│Metrics │ │ (logs) │ │ (analisis) │
│(metricas)│ │ ret. 30d │ │ + vuln detect │
│ ret. 1a │ │ │ │ │
└───┬──────┘ └─┬──────────┘ └──────┬───────────┘
│ │ │
┌───▼──────┐ │ │
│ vmagent │ │ │
│ (scrape) │ │ │
└───┬──────┘ │ │
│ │ │
╔═══╪══════════╪═════════════════════╪════════════════════════════════╗
║ │ │ │ ║
║ │ Cada servidor de produccion (PBS/PVE/Linux gestionado): ║
║ │ ║
║ │ ┌──────────────┐ ┌────────┐ ┌───────────┐ ┌───────────────┐ ║
║ │ │node_exporter │ │ Alloy │ │Wazuh Agent│ │CrowdSec │ ║
║ │ │(metricas) │ │(logs) │ │(FIM+HIDS) │ │+ Fail2ban │ ║
║ │ │ 5MB RAM │ │ 30-50MB│ │ 50-100MB │ │ ~80MB │ ║
║ │ │+textfile jobs│ │→ Loki │ │→ Wazuh Mgr│ │ │ ║
║ │ └──────────────┘ └────────┘ └───────────┘ └───────────────┘ ║
║ │ + auditd (ya instalado, 0 extra) ║
║ │ + SSH solo desde Bastion (iptables) ║
║ │ ║
║ │ Total overhead por servidor: ~200-250 MB RAM ║
╚═══╪═════════════════════════════════════════════════════════════════╝
│ Centralizados (VM monitoring):
│ ┌─────────────────────────────────┐
├──│ pbs-exporter (natrontech) │
│ │ - multi-target: todos los PBS │
│ │ - pve_exporter: clusters PVE │
│ └─────────────────────────────────┘
│ Complementarios:
│ ┌──────────────────┐ ┌──────────────────┐
│ │ Uptime Kuma │ │ Bastion SSH │
│ │ checks binarios │ │ grabacion sesion│
│ │ SSL, DNS, HTTP │ │ ProxyJump (-J) │
│ └──────────────────┘ └──────────────────┘
Inventario + Secretos:
┌──────────────────┐ ┌──────────────────┐
│ Netbox │ │ Vaultwarden │
│ SSOT ← ICSMgr │ │ secretos equipo │
│ → Ansible inv. │ │ (SSO Authentik) │
│ → Prometheus SD │ │ │
└──────────────────┘ └──────────────────┘
```
---
## Recursos totales del stack completo (Capa 1 + Capa 2)
### VMs centrales
| VM | Componentes | CPU | RAM | Disco |
|----|-----------|-----|-----|-------|
| **Monitoring** | VictoriaMetrics + vmagent + Loki + Grafana + Alertmanager + exporters | 4 vCPU | 8-12 GB | 200-500 GB |
| **Wazuh** | Wazuh Manager + Dashboard (OpenSearch) | 4 vCPU | 12 GB | 100 GB |
| **Zammad** | Zammad + PostgreSQL + Redis + Elasticsearch | 2-3 vCPU | 4-6 GB | 50 GB |
| **Authentik** | Authentik + PostgreSQL + Redis | 1-2 vCPU | 2-3 GB | 20 GB |
| **Netbox** | Netbox + PostgreSQL + Redis | 1-2 vCPU | 2-3 GB | 20 GB |
| **ICSManager** | ICSManager + PostgreSQL (todo Docker) | 1-2 vCPU | 2-3 GB | 20 GB |
| **WildDuck** | WildDuck + MongoDB + Redis (todo Docker) | 1-2 vCPU | 2-3 GB | 30-50 GB |
| **Outline** | Outline + PostgreSQL + Redis | 1 vCPU | 1-2 GB | 20 GB |
| **Servicios** | Semaphore + Uptime Kuma + Technitium + Traefik (stateless) | 1-2 vCPU | 2-3 GB | 20 GB |
| **Bastion** | SSH jump host + grabacion sesiones | 1 vCPU | 1 GB | 20 GB |
| **WG Hub A** | WireGuard hub master + wgdashboard + nftables + NAT CGNAT | 1 vCPU | 512 MB | 10 GB |
| **WG Hub B** | WireGuard hub backup (keepalived HA) | 1 vCPU | 512 MB | 10 GB |
| **PDM** | Proxmox Datacenter Manager (gestion multi-cluster) | 1 vCPU | 1-2 GB | 20 GB |
| **PMG** | Proxmox Mail Gateway (SpamAssassin + ClamAV + quarantine) | 1-2 vCPU | 2-3 GB | 30 GB |
| **Forgejo** | Ya desplegado (repos desarrollo + nuevos repos infra) | existente | existente | existente |
| **Total VMs centrales** | | **~24-32 vCPU** | **~44-58 GB** | **580-920 GB** |
Modelo: una VM = un servicio (caja negra). Cada servicio con BBDD tiene su propia VM dedicada. App + BBDD + Redis todo dentro. Se backupea, migra y diagnostica como unidad. PDM gestiona migracion cross-cluster. PMG filtra correo entrante/saliente delante de WildDuck.
Nota disco VM Monitoring: VictoriaMetrics (retencion 1 año, ~200GB) + Loki (retencion 30 dias, ~50-250GB segun servidores). A 500 servidores, considerar separar Loki en su propio disco/VM.
Infra de backup (Capa 6, ademas de las VMs centrales):
- PBS1 (Alemania): ya existente
- PBS2 (Polonia): servidor fisico nuevo por contratar (solo disco grande, CPU/RAM minimos)
- PBS3 (opcional por cliente premium): otro proveedor/pais, coste repercutido al cliente
- Replica oficina: servidor Xeon 96GB existente (coste 0)
Recomendacion servidor fisico: 128 GB RAM, 8-12c/16-24t, 2x2TB NVMe (ZFS mirror)
### En cada servidor de produccion (PBS/PVE/Linux)
| Componente | CPU | RAM |
|-----------|-----|-----|
| Grafana Alloy (logs) | <0.1 | 30-50 MB |
| node_exporter (metricas) | <0.1 | 5-10 MB |
| Wazuh Agent (FIM+HIDS) | <0.1 | 50-100 MB |
| CrowdSec | <0.1 | ~50 MB |
| Fail2ban | <0.1 | ~30 MB |
| auditd | ya instalado | 0 extra |
| pbs-jobs-metrics.sh (cron) | despreciable | despreciable |
| **Total por servidor** | **~0.2 vCPU** | **~200-250 MB** |
Frente a Checkmk/Zabbix agent que consume 200-500MB **solo para monitoring**, aqui tenemos monitoring + logs + HIDS + IPS por ~250MB. En servidores de 32-256GB, pasa completamente desapercibido.