Plan2026/06-decision-automatizacion.md

24 KiB

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:

# 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):

# 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.

# 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

# 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

# .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/"
# .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:

*/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