Plan2026/06-decision-automatizacion.md

543 lines
24 KiB
Markdown
Raw Normal View History

2026-03-14 19:33:00 +00:00
# Decision: Capa de automatizacion (punto 3)
## Contexto
Actualmente la operativa del PBS se gestiona con scripts bash en `/root/` (`modificar.sh`, `lista.sh`, `gc_jobs.sh`, `verify_jobs.sh`, `status_disk.sh`, etc.). Existe una clave SSH de `david@ansible` pero Ansible se ha usado muy poco (algun apt upgrade en batch). No hay experiencia real con playbooks ni roles. A 100-500 servidores, la forma actual no escala: scripts no versionados, sin idempotencia, sin audit trail, si el servidor se pierde se pierde toda la operativa.
Se desplegara un servidor nuevo desde cero donde se migraran VMs progresivamente. Ya existe un Forgejo con proyectos de desarrollo (solo git basico).
3 subcapas (Terraform/OpenTofu descartado):
- 3.1 Gestion de configuracion (Ansible + Semaphore)
- 3.2 Control de versiones y CI/CD (Forgejo existente + Actions)
- 3.3 Orquestacion de jobs (Semaphore + n8n futuro)
---
## 3.1 Gestion de configuracion: Ansible
### Por que Ansible y no otras
| Herramienta | Modelo | Agente | Estado |
|-------------|--------|--------|--------|
| **Ansible** | Push (sin agente) | Solo SSH | Estandar de facto para infra Linux |
| **Puppet** | Pull (con agente) | Si (puppet-agent, ~200MB RAM) | En declive, comunidad menguante |
| **Chef** | Pull (con agente) | Si (chef-client) | Comprado por Progress, futuro incierto |
| **Salt** | Push/Pull | Opcional (salt-minion) | Bueno pero menor ecosistema |
**Ansible gana por**:
- Ya se usa en el entorno (clave SSH de `david@ansible`)
- Sin agente: solo necesita SSH, que ya esta configurado en todos los servidores
- Inventario dinamico desde Netbox (ya decidido en 1.1)
- Enorme ecosistema de colecciones para Proxmox (`community.general.proxmox*`)
- Curva de aprendizaje accesible para sysadmins (YAML, no Ruby ni DSL propios)
- Compatible con todo lo decidido: Wazuh, VictoriaMetrics, Loki, CrowdSec, Fail2ban
### De modificar.sh a Ansible: la transformacion
El script `modificar.sh` actual hace en secuencia:
1. Crear dataset ZFS con quota y reservation (multiplicador 6x)
2. Crear datastore en PBS
3. Crear usuario PBS
4. Setear password
5. Configurar ACL (DatastorePowerUser)
6. Crear sync job (dia aleatorio 11-20, 09:XX)
7. Crear verify job (outdated 30d, ignore-verified)
8. Opcionalmente eliminar sync job si "migrado"
Esto se transforma en un **role de Ansible** (`pbs_customer`) con tasks idempotentes:
```yaml
# roles/pbs_customer/tasks/main.yml
---
- name: Crear dataset ZFS
community.general.zfs:
name: "pool9/{{ customer_name }}"
state: present
extra_zfs_properties:
quota: "{{ customer_size_gb * 6 }}G"
reservation: "{{ customer_size_gb * 5 }}G"
recordsize: "1M"
compression: "lz4"
atime: "off"
- name: Crear datastore PBS
ansible.builtin.uri:
url: "https://localhost:8007/api2/json/config/datastore"
method: POST
headers:
Authorization: "PBSAPIToken={{ pbs_api_token }}"
body_format: json
body:
name: "{{ customer_name }}"
path: "/pool9/{{ customer_name }}"
gc-schedule: "daily"
validate_certs: false
status_code: [200, 500] # 500 si ya existe
register: ds_result
- name: Crear usuario PBS
ansible.builtin.command:
cmd: >
proxmox-backup-manager user create
{{ customer_name }}@pbs
--comment "Cliente {{ customer_name }}"
register: user_result
failed_when: user_result.rc != 0 and 'already exists' not in user_result.stderr
- name: Setear password del usuario
ansible.builtin.command:
cmd: >
proxmox-backup-debug api set /access/password
--userid {{ customer_name }}@pbs
--password {{ customer_password }}
no_log: true # No mostrar password en logs
- name: Configurar ACL
ansible.builtin.command:
cmd: >
proxmox-backup-manager acl update
/datastore/{{ customer_name }}
DatastorePowerUser
--auth-id {{ customer_name }}@pbs
- name: Crear sync job
ansible.builtin.command:
cmd: >
proxmox-backup-manager sync-job create
{{ customer_name }}
--store {{ customer_name }}
--remote {{ sync_remote }}
--remote-store {{ customer_name }}
--schedule "*-{{ sync_day }} 09:{{ sync_minute }}"
--remove-vanished true
when: sync_server is defined and not sync_server.startswith('migrado')
register: sync_result
failed_when: sync_result.rc != 0 and 'already exists' not in sync_result.stderr
- name: Crear verify job
ansible.builtin.command:
cmd: >
proxmox-backup-manager verify-job create
v-{{ customer_name }}
--store {{ customer_name }}
--outdated-after 30
--ignore-verified true
register: verify_result
failed_when: verify_result.rc != 0 and 'already exists' not in verify_result.stderr
```
Variables desde Netbox (inventario dinamico):
```yaml
# host_vars o custom fields de Netbox
customer_name: "acme"
customer_size_gb: 100
customer_password: "{{ lookup('hashi_vault', 'secret/pbs/acme') }}" # Fase 2
sync_server: "pbs3343.vpn9.com.es"
sync_day: 15
sync_minute: 37
```
### Playbooks base (Dia 1)
| Playbook | Que hace | Prioridad |
|----------|----------|-----------|
| `hardening-ssh.yml` | SSH hardening completo + cloud-init fix + Fail2ban | **Dia 1** |
| `deploy-wazuh-agent.yml` | Instalar y registrar agente Wazuh | **Dia 1** |
| `deploy-node-exporter.yml` | node_exporter + Alloy para logs | Semana 1 |
| `deploy-auditd.yml` | Reglas auditd para PBS/Proxmox | Semana 1 |
| `deploy-crowdsec.yml` | CrowdSec con collections SSH + Linux | Semana 2 |
| `pbs-customer.yml` | Provisionar/modificar cliente PBS | Semana 2 |
| `pbs-maintenance.yml` | GC, verify, optimizar ZFS en batch | Mes 1 |
| `lynis-audit.yml` | Ejecutar Lynis y recoger informes | Mes 1 |
### Ansible UI: Semaphore vs AWX
| Aspecto | Semaphore | AWX |
|---------|-----------|-----|
| Recursos | 1 vCPU, 512MB RAM | 4 vCPU, 8GB RAM (Kubernetes) |
| Complejidad | Docker compose simple | Necesita K8s o docker-compose complejo |
| RBAC | Basico (admin/user) | Completo (orgs, teams, permisos por recurso) |
| Inventario dinamico | Via CLI plugin | Nativo con sync automatico |
| Scheduling | Si | Si (mas potente) |
| API | Basica | REST completa |
| Audit log | Si | Si (mas detallado) |
| Ideal para | Equipos de 2-5 personas | Equipos de 5-20+ personas |
**Recomendacion**: **Semaphore** para empezar. Si el equipo crece mas alla de 5 personas o necesitais RBAC granular, migrar a AWX.
```yaml
# docker-compose.yml de Semaphore
services:
semaphore:
image: semaphoreui/semaphore:latest
ports:
- "3000:3000"
environment:
SEMAPHORE_DB_DIALECT: bolt
SEMAPHORE_ADMIN_PASSWORD: "${SEMAPHORE_ADMIN_PASSWORD}"
SEMAPHORE_ADMIN_NAME: admin
SEMAPHORE_ADMIN_EMAIL: admin@empresa.com
SEMAPHORE_ADMIN: admin
volumes:
- ./data:/var/lib/semaphore
- ./config:/etc/semaphore
```
### Inventario dinamico desde Netbox
```yaml
# inventory/netbox.yml
plugin: netbox.netbox.nb_inventory
api_endpoint: https://netbox.empresa.local
token: "{{ lookup('env', 'NETBOX_TOKEN') }}"
validate_certs: false
# Agrupar por rol
group_by:
- device_roles
- platforms
- regions
- sites
- tenants
# Custom fields como variables de host
compose:
pbs_commercial_size_gb: custom_fields.pbs_commercial_size_gb
pbs_quota_gb: custom_fields.pbs_quota_gb
pbs_sync_server: custom_fields.pbs_sync_server
ansible_host: primary_ip4.address | split('/') | first
```
Flujo completo:
```
ICSManager → API → Netbox (crea device + custom fields)
Ansible lee inventario
Ejecuta playbook pbs-customer.yml
PBS provisionado + monitorizado
```
---
## 3.2 Control de versiones y CI/CD
### El problema
Todos los scripts operativos (`modificar.sh`, `lista.sh`, `gc_jobs.sh`, `verify_jobs.sh`, `status_disk.sh`, `enable-maintenance.sh`, etc.) estan en `/root/` sin control de versiones. Si:
- El servidor se pierde → se pierde toda la operativa
- Un tecnico modifica un script y rompe algo → no hay rollback
- Dos tecnicos modifican el mismo script → conflictos sin resolver
- No hay registro de quien cambio que ni cuando
### DECISION: Forgejo existente (ya desplegado)
Ya tenemos un Forgejo en produccion con proyectos de desarrollo. No hay que desplegar nada nuevo, solo ampliar su uso a infraestructura.
Funcionalidades actuales en uso: solo git basico (push, pull, branches).
Funcionalidades a activar: Forgejo Actions (CI/CD), branch protection, repos de infraestructura.
### Estructura de repositorios
```
empresa/
├── ansible-playbooks/ # Todos los playbooks y roles
│ ├── roles/
│ │ ├── pbs_customer/
│ │ ├── hardening_ssh/
│ │ ├── deploy_wazuh/
│ │ ├── deploy_node_exporter/
│ │ ├── deploy_alloy/
│ │ ├── deploy_crowdsec/
│ │ └── deploy_auditd/
│ ├── playbooks/
│ │ ├── site.yml
│ │ ├── pbs-customer.yml
│ │ ├── hardening.yml
│ │ └── maintenance.yml
│ ├── inventory/
│ │ └── netbox.yml
│ └── .forgejo/workflows/
│ └── lint.yml # ansible-lint en cada push
├── pbs-scripts/ # Scripts operativos (migrados de /root)
│ ├── gc_jobs.sh
│ ├── verify_jobs.sh
│ ├── status_disk.sh
│ └── pbs-mantenimiento-secuencial.sh
├── monitoring-config/ # Configuracion de monitoring
│ ├── grafana/dashboards/
│ ├── victoriametrics/
│ ├── vmagent/
│ ├── alertmanager/
│ └── loki/
├── docker-stacks/ # Docker compose de servicios
│ ├── dns-powerdns/
│ ├── vaultwarden/
│ ├── semaphore/
│ └── uptime-kuma/
└── docs/ # Documentacion como codigo
├── runbooks/
├── architecture/
└── onboarding/
```
### CI/CD con Forgejo Actions
```yaml
# .forgejo/workflows/ansible-lint.yml
name: Lint Ansible
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ansible-lint
uses: ansible/ansible-lint-action@v6
with:
path: "playbooks/"
```
```yaml
# .forgejo/workflows/deploy-hardening.yml
name: Deploy Hardening
on:
push:
branches: [main]
paths: ['roles/hardening_ssh/**', 'playbooks/hardening.yml']
jobs:
deploy:
runs-on: self-hosted # Runner en la VM de automatizacion
steps:
- uses: actions/checkout@v4
- name: Run hardening playbook
run: |
ansible-playbook -i inventory/netbox.yml playbooks/hardening.yml --diff
```
---
## 3.3 Infraestructura como codigo: DESCARTADO
### Terraform / OpenTofu / Pulumi: no aplica
Descartados porque:
- Los servidores son fisicos dedicados con VMs que viven meses o años
- No hay infraestructura efimera (crear/destruir continuamente)
- No hay multi-cloud (todo es Proxmox on-premise)
- Ansible cubre el 100% de lo que se necesita: configurar lo que ya existe
Si algun dia se necesita crear 50 VMs identicas de golpe (nueva sede, nuevo cluster), se puede reevaluar. Para ahora, añadiria complejidad sin beneficio.
---
## 3.4 Orquestacion de jobs
### El problema actual
Los cron jobs actuales:
```bash
*/15 * * * * /root/gc_jobs.sh >> /var/log/gc_jobs.log 2>&1
*/30 * * * * /root/verify_jobs.sh >> /var/log/verify_jobs.log 2>&1
```
Funcionan en un solo servidor. A 100+ PBS:
- No hay visibilidad de que jobs corren donde
- Si un job falla, nadie se entera (log en el servidor local)
- No hay forma de pausar/cancelar un job sin SSH al servidor
- No hay RBAC (quien puede ejecutar que)
- No hay dependencias entre jobs
**Caso real de congestion PBS**: los GC y verify jobs programados con el scheduling nativo de PBS provocaban picos de IO que congestionaban el servidor. Solucion actual: scripts "caseros" (`gc_jobs.sh`, `verify_jobs.sh`) que comprueban si hay otro proceso corriendo antes de lanzar uno nuevo, limitando la concurrencia manualmente.
### DECISION 3.4 (confirmada)
#### Semaphore como scheduler (reemplaza cron)
Si ya usamos Semaphore como UI de Ansible (decidido en 3.1), podemos usarlo tambien para scheduling de jobs:
- **GC jobs**: playbook que consulta API de PBS, comprueba si hay GC corriendo, si no lanza el mas antiguo. Cada 15min en Semaphore.
- **Verify jobs**: misma logica. Comprueba si hay verify activo, si no lanza el siguiente. Cada 30min.
- **Mantenimiento**: playbook de mantenimiento secuencial, ejecutable manualmente desde la UI
- **Hardening**: playbook de hardening, ejecutable con boton en la UI
La logica de control de concurrencia actual (no lanzar si ya hay uno corriendo) se traduce directamente:
1. **Dentro del playbook**: consulta API de PBS → si hay tarea activa, exit limpio
2. **En Semaphore**: opcion "no ejecutar si la ejecucion anterior no ha terminado"
3. Ambas combinadas = mismo comportamiento que los scripts actuales
Ventajas sobre cron:
- Visibilidad: ves en la UI que se ejecuto, cuando, y si fallo
- Notificaciones: si un GC o verify falla, webhook a Telegram
- Control: pausar/cancelar jobs desde la web sin SSH
- Audit log de quien ejecuto que y cuando
- Los playbooks estan en git (Forgejo)
#### n8n para workflows reactivos (futuro, mes 3-6)
Herramienta de tipo "si pasa X, haz Y" con editor visual de workflows. Apuntada como futura, no prioritaria.
| Trigger | Accion |
|---------|--------|
| Wazuh detecta fichero nuevo en /usr/bin | Enviar alerta Telegram + ejecutar audit playbook |
| PBS datastore > 90% quota | Notificar al comercial del cliente |
| Nuevo cliente en Netbox (ICSManager) | Ejecutar playbook pbs-customer.yml |
| Certificado SSL expira en 14 dias | Ejecutar renovacion + notificar |
| Sync job fallido 2 veces seguidas | Escalar a nivel 2 de soporte |
n8n tiene nodos de IA (OpenAI, Ollama local) que permiten analisis automatizado de eventos. Ejemplo: Wazuh detecta actividad sospechosa → n8n pasa el evento a un LLM → analiza si es falso positivo → si es real, alerta con explicacion en lenguaje claro.
**Cuando introducir**: cuando Ansible + Semaphore esten rodando y el equipo tenga soltura. No antes del mes 3.
---
## Resumen Capa 3 completa (DECISIONES FINALES)
```
┌─────────────────────────────────────────────────────┐
│ CAPA 3: AUTOMATIZACION │
├─────────────────────────────────────────────────────┤
│ │
│ 3.1 CONFIGURACION │
│ ┌───────────────────────────────────┐ │
│ │ Ansible (gestion de config) │ │
│ │ - Sin agente (solo SSH) │ │
│ │ - Roles: bootstrap, PBS customer,│ │
│ │ hardening, deploy agentes │ │
│ │ - Inventario: estatico → Netbox │ │
│ │ - UI: Semaphore (SSO Authentik) │ │
│ │ - API: integracion ICSManager │ │
│ └───────────────────────────────────┘ │
│ │
│ 3.2 VERSION CONTROL + CI/CD │
│ ┌───────────────────────────────────┐ │
│ │ Forgejo EXISTENTE (no desplegar) │ │
│ │ - Repos nuevos: infra/ansible, │ │
│ │ infra/pbs-scripts, infra/ │ │
│ │ monitoring-config, docker-stacks│ │
│ │ - Actions: lint ansible (mes 1-2)│ │
│ │ - Branch protection en main │ │
│ └───────────────────────────────────┘ │
│ │
│ 3.3 IaC: DESCARTADO │
│ ┌───────────────────────────────────┐ │
│ │ Terraform/OpenTofu/Pulumi: no │ │
│ │ Servidores permanentes, no cloud │ │
│ │ Ansible cubre el 100% │ │
│ └───────────────────────────────────┘ │
│ │
│ 3.4 ORQUESTACION (confirmada) │
│ ┌───────────────────────────────────┐ │
│ │ Semaphore (scheduling de jobs) │ │
│ │ - Reemplaza cron jobs │ │
│ │ - Control concurrencia PBS │ │
│ │ - Notif. fallos → Telegram │ │
│ │ n8n (workflows reactivos, mes 3+)│ │
│ │ - Futuro, con posible IA │ │
│ └───────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────┘
Recursos adicionales:
- Semaphore: contenedor Docker en VM servicios, 512 MB RAM
- Forgejo: YA DESPLEGADO (no requiere recursos nuevos)
- n8n (futuro): contenedor Docker, 512 MB RAM
```
---
## Prioridades de despliegue Capa 3
Contexto: equipo con experiencia minima en Ansible (solo apt upgrade en batch). Forgejo ya existe con proyectos de desarrollo. Servidor nuevo desde cero.
| Prioridad | Que | Esfuerzo | Impacto |
|-----------|-----|----------|---------|
| **Dia 1** | Crear repos infra en Forgejo existente | 1h | Scripts versionados desde el minuto 0 |
| **Dia 1** | Migrar scripts de /root/ a repo git | 1h | Backup de toda la operativa |
| **Semana 1** | Primer playbook: apt upgrade en todos (lo que ya sabian) | 2h | Confianza con Ansible |
| **Semana 1** | Playbook bootstrap-server.yml (hardening + agentes) | 4h | Cada servidor nuevo nace seguro |
| **Semana 2** | Instalar Semaphore (SSO Authentik) | 2h | UI web para ejecutar playbooks |
| **Semana 2** | Playbook deploy-wazuh-agent.yml | 2h | Wazuh desplegado en masa |
| **Mes 1** | Inventario dinamico Netbox → Ansible | 1 dia | Fuente unica de verdad |
| **Mes 1** | Role pbs_customer (reemplazar modificar.sh) | 1 dia | Provisionamiento idempotente |
| **Mes 1** | Migrar cron jobs a Semaphore scheduling | 2h | Audit log, notificaciones de fallo |
| **Mes 1-2** | Forgejo Actions: ansible-lint en cada push | 2h | Validacion automatica |
| **Mes 1-2** | Branch protection en repos de infra | 30min | Review obligatorio |
| **Mes 3+** | n8n para workflows reactivos | 1 dia | Automatizacion de respuestas a eventos |
---
## Diagrama del stack completo hasta ahora (Capas 1-3)
```
┌──────────────────┐
│ GRAFANA │
│ (UI unificada) │
└────────┬─────────┘
┌───────────────────┼───────────────────┐
│ │ │
┌─────────▼─────────┐ ┌──────▼──────┐ ┌─────────▼─────────┐
│ VictoriaMetrics │ │ Loki │ │ Wazuh Dashboard │
│ (metricas) │ │ (logs) │ │ (seguridad) │
└─────────┬─────────┘ └──────┬──────┘ └─────────┬─────────┘
│ │ │
┌─────────▼─────────┐ │ ┌─────────▼─────────┐
│ vmagent (scraper) │ │ │ Wazuh Manager │
└─────────┬─────────┘ │ └─────────┬─────────┘
│ │ │
╔══════════╪═══════════════════╪════════════════════╪══════════╗
║ Cada servidor de produccion: ║
║ ┌──────────────┐ ┌────────┐ ┌───────────┐ ┌─────────────┐ ║
║ │node_exporter │ │ Alloy │ │Wazuh Agent│ │ CrowdSec │ ║
║ │(metricas) │ │(logs) │ │(FIM+HIDS) │ │ + Fail2ban │ ║
║ │ 5MB RAM │ │ 30-50MB│ │ 50-100MB │ │ ~80MB │ ║
║ └──────────────┘ └────────┘ └───────────┘ └─────────────┘ ║
║ + auditd (ya instalado, 0 extra) ║
║ Total overhead por servidor: ~200-250MB RAM ║
╚═════════════════════════════════════════════════════════════╝
┌─────────────────┐ ┌──────────────────┐
│ Netbox (CMDB) │────▶│ Ansible │
│ ← ICSManager │ │ + Semaphore (UI) │
└─────────────────┘ │ + Forgejo (git) │
└──────────────────┘
┌─────────────────┐ ┌──────────────────┐
│ Uptime Kuma │ │ Vaultwarden │
│ (checks binarios│ │ (passwords equipo│
└─────────────────┘ └──────────────────┘
VMs centrales (modelo: 1 VM = 1 servicio, caja negra):
- VM Monitoring: 4 vCPU, 8-12 GB RAM, 200-500 GB disco
- VM Wazuh: 4 vCPU, 12 GB RAM, 100 GB disco
- VM Zammad: 2-3 vCPU, 4-6 GB RAM, 50 GB disco
- VM Authentik: 1-2 vCPU, 2-3 GB RAM, 20 GB disco
- VM Netbox: 1-2 vCPU, 2-3 GB RAM, 20 GB disco
- VM ICSManager: 1-2 vCPU, 2-3 GB RAM, 20 GB disco
- VM WildDuck: 1-2 vCPU, 2-3 GB RAM, 30-50 GB disco
- VM Outline: 1 vCPU, 1-2 GB RAM, 20 GB disco
- VM Servicios: 1-2 vCPU, 2-3 GB RAM, 20 GB disco
(Semaphore + Uptime Kuma + Technitium + Traefik, stateless)
- VM Bastion: 1 vCPU, 1 GB RAM, 20 GB disco
- VM WG Hub A: 1 vCPU, 512 MB RAM, 10 GB disco (+ NAT CGNAT)
- VM WG Hub B: 1 vCPU, 512 MB RAM, 10 GB disco
- VM PDM: 1 vCPU, 1-2 GB RAM, 20 GB disco (Proxmox Datacenter Manager)
- VM PMG: 1-2 vCPU, 2-3 GB RAM, 30 GB disco (Proxmox Mail Gateway)
- VM Forgejo: YA DESPLEGADA
- PowerDNS: ya existente
- PBS1 (Alemania): ya existente
- PBS2 (Polonia): servidor fisico nuevo
- PBS3 (opcional): por cliente premium
- Replica oficina: servidor Xeon 96GB existente (coste 0)
Total infra de gestion: ~24-32 vCPU, ~44-58 GB RAM, ~580-920 GB disco
Recomendacion servidor: 128 GB RAM, 8-12c/16-24t, 2x2TB NVMe
```