# Protegiendo nuestras API's de manera inteligente.

### Introducción

Generé una API Dummy de cuentas bancarias y para protegerla vamos a tener API Manager **Kong** al frente, reportando los log’s de las consultas que recibe a **Wazuh**. Si tenemos coincidencia con algunas de las reglas que cargaremos en **Wazuh**, se responderá al evento. Esa respuesta será dada por **N8N**, ejecutando un flujo de trabajo donde realizaremos diferentes pasos, como agregar la dirección del atacante en una lista negra de **Kong** o enviar un mensaje o, por qué no, levantar el caso en **The Hive**. Como si fuera poco agregamos **Konga** para la gestión grafica de **Kong**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1759947281244/72a9bf01-0ed4-4d91-bcd4-23e0f6410484.png align="center")

### Arquitectura

Antes de eso deberían descargar la configuración de Wazuh Docker, para esta ocasión uso *single-node-stack.*

```bash
git clone https://github.com/wazuh/wazuh-docker.git -b v4.13.1
```

Generamos los certificados.

```bash
cd wazuh-docker/single-node/
docker compose -f generate-indexer-certs.yml run --rm generator
```

Una vez que lo corramos, podemos modificar le *docker-compose* original, con todo el stack necesario para esta prueba de concepto.

```yaml
# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
services:
  wazuh.manager:
    image: wazuh/wazuh-manager:4.13.1
    hostname: wazuh.manager
    restart: always
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 655360
        hard: 655360
    ports:
      - "1514:1514"
      - "1515:1515"
      - "514:514/udp"
      - "55000:55000"
    environment:
      - INDEXER_URL=https://wazuh.indexer:9200
      - INDEXER_USERNAME=admin
      - INDEXER_PASSWORD=SecretPassword
      - FILEBEAT_SSL_VERIFICATION_MODE=none
      - SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
      - SSL_CERTIFICATE=/etc/ssl/filebeat.pem
      - SSL_KEY=/etc/ssl/filebeat.key
      - API_USERNAME=wazuh-wui
      - API_PASSWORD=MyS3cr37P450r.*-
    volumes:
      - wazuh_api_configuration:/var/ossec/api/configuration
      - wazuh_etc:/var/ossec/etc
      - wazuh_logs:/var/ossec/logs
      - wazuh_queue:/var/ossec/queue
      - wazuh_var_multigroups:/var/ossec/var/multigroups
      - wazuh_integrations:/var/ossec/integrations
      - wazuh_active_response:/var/ossec/active-response/bin
      - wazuh_agentless:/var/ossec/agentless
      - wazuh_wodles:/var/ossec/wodles
      - filebeat_etc:/etc/filebeat
      - filebeat_var:/var/lib/filebeat
      - ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.manager.pem:/etc/ssl/filebeat.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem:/etc/ssl/filebeat.key
      - ./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf
      - ./config/wazuh_cluster/kong-rules.xml:/var/ossec/etc/rules/kong-rules.xml:ro
      - ./config/wazuh_cluster/0374-kong-decoder.xml:/var/ossec/etc/decoders/0374-kong-decoder.xml:ro # Agrego el decoder para Kong.
      - ./config/wazuh_cluster/custom-n8n:/var/ossec/integrations/custom-n8n:ro

    networks:
      - poc-network
  wazuh.indexer:
    image: wazuh/wazuh-indexer:4.13.1
    hostname: wazuh.indexer
    restart: always
    ports:
      - "9200:9200"
    environment:
      - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    volumes:
      - wazuh-indexer-data:/var/lib/wazuh-indexer
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer.key
      - ./config/wazuh_indexer_ssl_certs/wazuh.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh.indexer.pem
      - ./config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/certs/admin.pem
      - ./config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/certs/admin-key.pem
      - ./config/wazuh_indexer/wazuh.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
      - ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml
    networks:
      - poc-network
  wazuh.dashboard:
    image: wazuh/wazuh-dashboard:4.13.1
    hostname: wazuh.dashboard
    restart: always
    ports:
      - 443:5601
    environment:
      - INDEXER_USERNAME=admin
      - INDEXER_PASSWORD=SecretPassword
      - WAZUH_API_URL=https://wazuh.manager
      - DASHBOARD_USERNAME=kibanaserver
      - DASHBOARD_PASSWORD=kibanaserver
      - API_USERNAME=wazuh-wui
      - API_PASSWORD=MyS3cr37P450r.*-
    volumes:
      - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
      - ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
      - ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
      - ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
      - ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
      - wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config
      - wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
    depends_on:
      - wazuh.indexer
    links:
      - wazuh.indexer:wazuh.indexer
      - wazuh.manager:wazuh.manager
    networks:
      - poc-network

# ============================================
# KONG DATABASE
# ============================================
  kong-database:
    image: postgres:15-alpine
    container_name: kong-database
    restart: always
    networks:
      - poc-network
    environment:
      POSTGRES_USER: kong
      POSTGRES_DB: kong
      POSTGRES_PASSWORD: kongpass
    ports:
      - "5432:5432"
    volumes:
      - kong-database-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "kong"]
      interval: 10s
      timeout: 5s
      retries: 5

# ============================================
# KONG MIGRATIONS
# ============================================
  kong-migrations:
    image: kong:3.5
    container_name: kong-migrations
    command: kong migrations bootstrap
    networks:
      - poc-network
    environment:
      KONG_DATABASE: postgres
      KONG_PG_HOST: kong-database
      KONG_PG_USER: kong
      KONG_PG_PASSWORD: kongpass
      KONG_PG_DATABASE: kong
    depends_on:
      kong-database:
        condition: service_healthy
    restart: on-failure

# ============================================
# KONG API GATEWAY
# ============================================
  kong:
      image: kong:3.5
      container_name: kong
      restart: unless-stopped
      networks:
        - poc-network
      environment:
        KONG_DATABASE: postgres
        KONG_PG_HOST: kong-database
        KONG_PG_USER: kong
        KONG_PG_PASSWORD: kongpass
        KONG_PG_DATABASE: kong
        KONG_PROXY_ACCESS_LOG: /dev/stdout
        KONG_ADMIN_ACCESS_LOG: /dev/stdout
        KONG_PROXY_ERROR_LOG: /dev/stderr
        KONG_ADMIN_ERROR_LOG: /dev/stderr
        KONG_ADMIN_LISTEN: 0.0.0.0:8001
        KONG_ADMIN_GUI_LISTEN: 0.0.0.0:8002
        KONG_PROXY_LISTEN: 0.0.0.0:8000, 0.0.0.0:8443 ssl
      ports:
        - "8000:8000"
        - "8443:8443"
        - "8001:8001"
        - "8002:8002"
      depends_on:
        kong-database:
          condition: service_healthy
        kong-migrations:
          condition: service_completed_successfully
      healthcheck:
        test: ["CMD", "kong", "health"]
        interval: 10s
        timeout: 10s
        retries: 10

# ============================================
# KONGA DATABASE
# ============================================
  konga-database:
    image: postgres:9.6-alpine
    container_name: konga-database
    restart: always
    networks:
      - poc-network
    environment:
      POSTGRES_USER: konga
      POSTGRES_DB: konga
      POSTGRES_PASSWORD: kongapass
    ports:
      - "5433:5432"
    volumes:
      - konga-database-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "konga"]
      interval: 10s
      timeout: 5s
      retries: 5

# ============================================
# KONGA PREPARE
# ============================================
  konga-prepare:
    image: pantsel/konga:latest
    container_name: konga-prepare
    command: "-c prepare -a postgres -u postgresql://konga:kongapass@konga-database:5432/konga"
    networks:
      - poc-network
    environment:
      DB_ADAPTER: postgres
      DB_HOST: konga-database
      DB_USER: konga
      DB_PASSWORD: kongapass
      DB_DATABASE: konga
    depends_on:
      konga-database:
        condition: service_healthy
    restart: on-failure

# ============================================
# KONGA GUI
# ============================================
  konga:
    image: pantsel/konga:latest
    container_name: konga
    restart: always
    networks:
      - poc-network
    environment:
      DB_ADAPTER: postgres
      DB_HOST: konga-database
      DB_PORT: 5432
      DB_USER: konga
      DB_PASSWORD: kongapass
      DB_DATABASE: konga
      NODE_ENV: production
      KONGA_HOOK_TIMEOUT: 120000
      DB_PG_SCHEMA: public
      TOKEN_SECRET: change-this-secret-token
    ports:
      - "1337:1337"
    depends_on:
      konga-database:
        condition: service_healthy
      konga-prepare:
        condition: service_completed_successfully
      kong:
        condition: service_healthy

# ============================================
# BANKING API
# ============================================
  banking-api:
    image: safernandez666/banking-api
    container_name: banking-api
    restart: always
    networks:
      - poc-network
    ports:
      - "3000:3000"
    volumes:
      - banking-data:/app

# ============================================
# LOGSPOUT - LOG FORWARDER
# ============================================
  logspout:
    image: gliderlabs/logspout:latest
    container_name: logspout
    restart: always
    networks:
      - poc-network
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    command: syslog://wazuh.manager:514
    environment:
      - SYSLOG_FORMAT=rfc3164
      - LOGSPOUT=ignore
      - FILTER_NAME=kong|banking-api  # ← AGREGAR: Solo Kong y Banking API
    depends_on:
      - wazuh.manager
# ============================================
# N8N
# ============================================
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    ports:
      - "5678:5678"
    environment:
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=n8n_password
      - N8N_HOST=0.0.0.0
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - WEBHOOK_URL=http://n8n:5678/
      - GENERIC_TIMEZONE=America/Argentina/Buenos_Aires
    volumes:
      - n8n_data:/home/node/.n8n
    networks:
      - poc-network
# ============================================
# OLLAMA
# ============================================
  ollama:
    image: ollama/ollama:latest
    container_name: ollama
    ports:
      - "11435:11434" # Expose Ollama's API port
    volumes:
      - ollama_data:/root/.ollama # Persistent storage for models and data
    restart: unless-stopped
    networks:
      - poc-network
# ============================================
# NETWORKS
# ============================================
networks:
  poc-network:
    driver: bridge

# ============================================
# VOLUMES
# ============================================
volumes:
  wazuh_api_configuration:
  wazuh_etc: 
  wazuh_logs:
  wazuh_queue:
  wazuh_var_multigroups:
  wazuh_integrations:
  wazuh_active_response:
  wazuh_agentless:
  wazuh_wodles:
  filebeat_etc:
  filebeat_var:
  wazuh-indexer-data:
  wazuh-dashboard-config:
  wazuh-dashboard-custom:
  kong-database-data:
  konga-database-data:
  banking-data:
  n8n_data:
  ollama_data:
```

Importante edistar `./config/wazuh_cluster/wazuh_manager.conf,` para que lo tome Wazuh Manager como su `ossec.conf`. Le agregamos esto:

```xml
  <!-- ============================================ -->
  <!-- Configuración de receptor remoto (Syslog) -->
  <!-- ============================================ -->
  <remote>
    <connection>syslog</connection>
    <port>514</port>
    <protocol>udp</protocol>
    <allowed-ips>any</allowed-ips>
  </remote>

  <!-- ============================================ -->
  <!-- INTEGRACIÓN CON N8N - WEBHOOKS PARA KONG -->
  <!-- ============================================ -->
  
  <!-- Webhook específico para ATAQUES (nivel 8+) -->
  <integration>
    <name>custom-n8n</name>
    <level>8</level>
    <group>kong,attack</group>
    <alert_format>json</alert_format>
  </integration>
```

También vamos agregar la integración `custom-n8n` en `./config/wazuh_cluster/`.

```bash
#!/bin/bash
# Wazuh to n8n Integration

N8N_WEBHOOK_URL="http://n8n:5678/webhook/wazuh-kong-alerts"
LOG_FILE="/var/ossec/logs/integrations.log"

# Wazuh pasa el archivo de alerta como primer argumento
ALERT_FILE="$1"

# Timestamp
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# Log
echo "[${TIMESTAMP}] Processing alert from file: ${ALERT_FILE}" >> ${LOG_FILE}

# Leer el contenido del archivo
if [ -f "${ALERT_FILE}" ]; then
    ALERT_JSON=$(cat "${ALERT_FILE}")
    
    # Enviar a n8n
    HTTP_CODE=$(curl -X POST "${N8N_WEBHOOK_URL}" \
      -H "Content-Type: application/json" \
      -d "${ALERT_JSON}" \
      -w "%{http_code}" \
      -s \
      -o /dev/null \
      --connect-timeout 10 \
      --max-time 30)
    
    if [ "${HTTP_CODE}" == "200" ] || [ "${HTTP_CODE}" == "201" ]; then
        echo "[${TIMESTAMP}] SUCCESS - HTTP ${HTTP_CODE}" >> ${LOG_FILE}
        exit 0
    else
        echo "[${TIMESTAMP}] FAILED - HTTP ${HTTP_CODE}" >> ${LOG_FILE}
        exit 1
    fi
else
    echo "[${TIMESTAMP}] ERROR - Alert file not found: ${ALERT_FILE}" >> ${LOG_FILE}
    exit 1
fi
```

> Es muy importante que a custom-n8n le hagan un chmod 555, para que pueda ser ejecutado por cualquiera. Esto en local, antes de levantar el contenedor.

Agregamos el **docoder** y las **regla**, también en `/config/wazuh_cluster`. El **decoder** se llamara `0374-kong-decoder.xml` y las **regla** `kong-rules.xml`.

```xml
<!--
  -  Kong API Manager custom decoder
  -  Created for extracting srcip from Kong logs
  -  Author: Santiago Fernandez
  -  Date: October 2025
  -
  -  This decoder extracts:
  -    - srcip: Source IP address
  -    - protocol: HTTP method (GET, POST, etc.)
  -    - url: Requested URL
  -    - id: HTTP status code
-->

<decoder name="kong-decoder">
    <prematch>^\S+ \S+ kong[\d+]:</prematch>
    <regex>^\S+ \S+ kong[\d+]: (\S+) \S+ \S+ [\S+ \S+] "(\w+) (\S+) HTTP\S+" (\d+) \d+ \S+ \S+</regex>
    <order>srcip, protocol, url, id</order>
</decoder>
```

```xml
<!--
  -  Kong API Gateway Rules
  -  Author: Santiago Fernandez
  -  Date: October 2025
  -  
  -  Rule ID Range: 100100 - 100199
-->

<group name="kong,">

  <!-- ============================================ -->
  <!-- REGLA BASE - Cualquier tráfico de Kong -->
  <!-- ============================================ -->
  <rule id="100100" level="3">
    <decoded_as>kong-decoder</decoded_as>
    <description>Kong: API Gateway traffic detected</description>
    <group>kong,access</group>
  </rule>

  <!-- ============================================ -->
  <!-- REGLAS PARA BANKING API -->
  <!-- ============================================ -->

  <!-- Accesos exitosos (2xx) a banking -->
  <rule id="100101" level="5">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^2\d\d$</id>
    <description>Kong: Successful access to banking API - $(protocol) $(url) - Status: $(id) from $(srcip)</description>
    <group>kong,banking,success</group>
  </rule>

  <!-- Errores del cliente (4xx) en banking -->
  <rule id="100102" level="7">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^4\d\d$</id>
    <description>Kong: Client error on banking API - $(protocol) $(url) - Status: $(id) from $(srcip)</description>
    <group>kong,banking,error,client_error</group>
  </rule>

  <!-- Errores del servidor (5xx) en banking -->
  <rule id="100103" level="10">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^5\d\d$</id>
    <description>Kong: Server error on banking API - $(protocol) $(url) - Status: $(id)</description>
    <group>kong,banking,critical,server_error</group>
  </rule>

  <!-- ============================================ -->
  <!-- REGLAS DE CORRELACIÓN - Detección de ataques -->
  <!-- ============================================ -->

  <!-- Múltiples errores 4xx desde la misma IP (posible escaneo) -->
  <rule id="100104" level="8" frequency="5" timeframe="60">
    <if_matched_sid>100102</if_matched_sid>
    <same_source_ip />
    <description>Kong: Multiple failed requests ($(id)) from $(srcip) to banking API - Possible scanning or brute force attempt</description>
    <group>kong,banking,attack,multiple_errors,scanning</group>
  </rule>

  <!-- Múltiples errores 5xx (problema en el servidor) -->
  <rule id="100105" level="9" frequency="3" timeframe="120">
    <if_matched_sid>100103</if_matched_sid>
    <description>Kong: Multiple server errors on banking API - Service may be down or experiencing issues</description>
    <group>kong,banking,critical,service_degradation</group>
  </rule>

  <!-- ============================================ -->
  <!-- ENDPOINTS SENSIBLES -->
  <!-- ============================================ -->

  <!-- Acceso a endpoints de cuentas -->
  <rule id="100106" level="6">
    <if_sid>100101</if_sid>
    <url>/banking/cuentas</url>
    <description>Kong: Access to accounts endpoint - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,sensitive,accounts</group>
  </rule>

  <!-- Acceso a endpoints de transacciones -->
  <rule id="100107" level="6">
    <if_sid>100101</if_sid>
    <url>/banking/transacciones</url>
    <description>Kong: Access to transactions endpoint - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,sensitive,transactions</group>
  </rule>

  <!-- Acceso a endpoints de transferencias -->
  <rule id="100108" level="7">
    <if_sid>100101</if_sid>
    <url>/banking/transferencias</url>
    <description>Kong: Access to money transfer endpoint - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,sensitive,transfers,high_risk</group>
  </rule>

  <!-- ============================================ -->
  <!-- MÉTODOS HTTP ESPECÍFICOS -->
  <!-- ============================================ -->

  <!-- Peticiones DELETE en banking (alta sensibilidad) -->
  <rule id="100110" level="8">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <protocol>DELETE</protocol>
    <description>Kong: DELETE request on banking API - $(url) from $(srcip)</description>
    <group>kong,banking,modification,delete,high_risk</group>
  </rule>

  <!-- Peticiones PUT/PATCH en banking -->
  <rule id="100111" level="6">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <protocol>PUT|PATCH</protocol>
    <description>Kong: Modification request on banking API - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,modification</group>
  </rule>

  <!-- Peticiones POST en banking -->
  <rule id="100112" level="5">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <protocol>POST</protocol>
    <id>^2\d\d$</id>
    <description>Kong: POST request on banking API - $(url) from $(srcip)</description>
    <group>kong,banking,creation</group>
  </rule>

  <!-- ============================================ -->
  <!-- CÓDIGOS DE ESTADO ESPECÍFICOS -->
  <!-- ============================================ -->

  <!-- 401 - No autorizado -->
  <rule id="100120" level="7">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^401$</id>
    <description>Kong: Unauthorized access attempt to banking API - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,authentication,unauthorized</group>
  </rule>

  <!-- Múltiples 401 desde la misma IP -->
  <rule id="100121" level="9" frequency="3" timeframe="60">
    <if_matched_sid>100120</if_matched_sid>
    <same_source_ip />
    <description>Kong: Multiple unauthorized access attempts from $(srcip) - Possible credential stuffing attack</description>
    <group>kong,banking,attack,authentication,credential_stuffing</group>
  </rule>

  <!-- 403 - Prohibido -->
  <rule id="100122" level="7">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^403$</id>
    <description>Kong: Forbidden access attempt to banking API - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,authorization,forbidden</group>
  </rule>

  <!-- 404 - No encontrado -->
  <rule id="100123" level="5">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^404$</id>
    <description>Kong: Resource not found on banking API - $(protocol) $(url) from $(srcip)</description>
    <group>kong,banking,not_found</group>
  </rule>

  <!-- Múltiples 404 desde la misma IP (posible escaneo de endpoints) -->
  <rule id="100124" level="8" frequency="10" timeframe="120">
    <if_matched_sid>100123</if_matched_sid>
    <same_source_ip />
    <description>Kong: Multiple 404 errors from $(srcip) - Possible endpoint enumeration attack</description>
    <group>kong,banking,attack,enumeration</group>
  </rule>

  <!-- 429 - Too Many Requests (Rate Limiting) -->
  <rule id="100125" level="6">
    <if_sid>100100</if_sid>
    <url>/banking/</url>
    <id>^429$</id>
    <description>Kong: Rate limit exceeded on banking API from $(srcip)</description>
    <group>kong,banking,rate_limit,abuse</group>
  </rule>

  <!-- ============================================ -->
  <!-- TRÁFICO NO BANKING (Otros endpoints) -->
  <!-- ============================================ -->

  <!-- Accesos exitosos a otros endpoints -->
  <rule id="100130" level="3">
    <if_sid>100100</if_sid>
    <id>^2\d\d$</id>
    <description>Kong: Successful request - $(protocol) $(url) from $(srcip)</description>
    <group>kong,success</group>
  </rule>

  <!-- Errores en otros endpoints -->
  <rule id="100131" level="5">
    <if_sid>100100</if_sid>
    <id>^4\d\d$</id>
    <description>Kong: Client error - $(protocol) $(url) - Status: $(id) from $(srcip)</description>
    <group>kong,error</group>
  </rule>

</group>
```

Listo! Ya estamos para ejecutar.

```bash
docker-compose up -d
```

Vamos a pegarle una mirada a los contenedores.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1759873875228/83d085d5-2cc0-4ba2-8bff-fe93def454f1.png align="center")

Para que tengamos una idea de los puertos y contenedores, te los paso en limpio aquí.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1759874531750/b7f8bc64-5dd4-4e5c-876b-3d9a8a66bd5c.png align="center")

**Banking API**

Es una API generada para este Prueba de Concepto con información ficticia. Pueden encontrar el código en el repositorio, pero a grandes rasgos nos data esta información:

* Gestiona **clientes** (nombre, email, teléfono, fecha de registro).
    
* Gestiona **cuentas** vinculadas a clientes (número, tipo, saldo, moneda).
    
* Gestiona **tarjetas de crédito** asociadas a cuentas (número, límite, saldo usado, vencimiento). En las respuestas públicas el CVV se oculta.
    
* Registra **transacciones** (depósitos, retiros, transferencias, compras).
    
* Provee un **resumen** por cliente que agrega cuentas y tarjetas (totales de saldos y crédito disponible).
    
* Incluye endpoints de salud y la documentación.
    

### Kong

Voy a proporcionarles los comandos para que nuestro API Manager consuma la Banking API. Vamos paso a paso: primero, debemos crear un "**Service**" en Kong que represente tu API backend.

```bash
curl -i -X POST http://localhost:8001/services/ \
  --data "name=banking-api" \
  --data "url=http://banking-api:3000"
```

Creamos un "**Route**" que define cómo los clientes accederán a tu servicio. Vincula la ruta al servicio creado anteriormente.

```bash
curl -i -X POST http://localhost:8001/services/banking-api/routes \
  --data "name=banking-route" \
  --data "paths[]=/banking" \
  --data "strip_path=true"
```

Habilitamos CORS (Cross-Origin Resource Sharing) para permitir peticiones desde navegadores. Aca lo voy a crear de una forma permisiva.

```bash
curl -i -X POST http://localhost:8001/services/banking-api/plugins \
  --data "name=cors" \
  --data "config.origins=*" \
  --data "config.methods[]=GET" \
  --data "config.methods[]=HEAD" \
  --data "config.methods[]=PUT" \
  --data "config.methods[]=PATCH" \
  --data "config.methods[]=POST" \
  --data "config.methods[]=DELETE" \
  --data "config.methods[]=OPTIONS" \
  --data "config.headers[]=*" \
  --data "config.exposed_headers[]=*" \
  --data "config.credentials=true" \
  --data "config.max_age=3600"
```

Como saben, **Kong** es un API Manager. Podríamos habilitar varios plugins, pero para la PoC no son necesarios. Podrías habilitar *Rate Limiting*, *Request Size Limiting*, *Correlation ID* o *Request Transformer*. Vamos a probarlo.

```bash
# Vamos a ver los clientes
curl http://localhost:8000/banking/clientes | jq
# Cliente con ID 1
curl http://localhost:8000/banking/clientes/1 | jq
```

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760013338567/7f83eb76-ef89-4b62-9f23-129664f169f8.png align="center")

### Konga

Todo lo que hicimos con curl, podemos hacerlo a través de la interfaz gráfica. Primero, vamos a crear nuestra cuenta en Konga y luego agregar la conexión para gestionar Kong.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760011864340/f1626da3-723c-4cfc-9e73-fe1143bcda85.png align="center")

Ahí se ve todo lo creado, anteriormente. Por ejemplo el servicio.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760013401577/b0b166a5-97c5-45ad-baef-fe6fd8f0ebb1.png align="center")

o la ruta.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760013433243/d1441bda-7e29-49b4-b904-c2b686e68160.png align="center")

### Wazuh

Si todo salió bien, ingresamos a Wazuh para ver nuestras consultas. Para ver las reglas de Kong, aplique un filtro `decoder.name: kong-decoder`

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760016132951/8ac5949b-791f-4867-aae4-ba7402088fe5.png align="center")

### N8N

Ya levantado el contenedor ingresamos a http://localhost:5678. Ahí armaremos dos workflows, uno para el análisis y otro para que los administradores puedan liberar la ip bloqueada.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760126072194/1c2b31ee-d8b2-4898-a017-c346f32bc996.png align="center")

Una vez que suframos el ataque, habrá lógica para decidir si debemos bloquear o no. Esto es una prueba, pero imagino consultas de diferentes API, Slack o servicios internos. Una vez que se ejecute el **Workflow Block IP**, deberíamos recibir un correo como este:

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760126358580/2ab5b93c-cf33-4304-84c7-e7a57e760cff.png align="center")

Al final del correo, podemos invocar el Workflow Release IP para liberar la dirección del atacante.

### Simulacion de Ataque

Les dejo un script para simular un ataque rápidamente.

```basic
#!/bin/bash
# quick-test.sh - Prueba rpida

echo " Generando alertas de prueba..."

# Trfico normal
curl -s http://localhost:8000/banking/cuentas

# Mltiples 404 (activar regla de correlacin)
for i in {1..12}; do
    curl -s http://localhost:8000/banking/fake-endpoint-$i
    sleep 0.5
done

# Mltiples 401 (credential stuffing)
for i in {1..5}; do
    curl -s http://localhost:8000/banking/cuentas -H "Authorization: Bearer fake_$i"
    sleep 1
done

# DELETE peligroso
curl -s -X DELETE http://localhost:8000/banking/cuentas/123

echo " Alertas generadas. Revisa n8n y Wazuh!"
```

Espero que les sirva y puedan usarlo en sus empresas, si necesitan una mano me avisan.

Saludos, Santi.

### Bonus Track

Dejo los Worflows de N8N para analizar con Ollama. [Código](https://drive.google.com/drive/folders/1LBsMlKYLv9mm-veLfMY4VplLfZDcGqcH?usp=sharing).

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760386429641/7f13f9b5-0dfd-429a-b3d2-7379097e2385.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760386420742/f8e19da7-4757-411e-ad88-70d1587dff25.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760386398276/a101fb50-2d7b-41c1-8f3f-a149d593ba1e.png align="center")

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1760386359883/6eb2f93b-4e06-4d4a-8810-babe3ef7eb5a.png align="center")

### Comandos Útiles

```basic
# 1. Reiniciar Wazuh Manager
docker-compose restart single-node-wazuh.manager-1

# 2. Verificar que está escuchando en el puerto 514
docker exec single-node-wazuh.manager-1 netstat -tlnp | grep 514

# 3. Revisar ossec.conf
docker exec single-node-wazuh.manager-1 cat /var/ossec/etc/ossec.conf

# 4. Ver logs de Integraciones
docker exec single-node-wazuh.manager-1 tail -f /var/ossec/logs/integrations.log

# 5. Ver logs de Alarmas de Kong
docker exec -it single-node-wazuh.manager-1 tail -f /var/ossec/logs/alerts/alerts.log | grep -A 10 "kong"

# 6. Ver logs de Wazuh Manager
docker logs -f single-node-wazuh.indexer-1
```

### Referencias

[https://ipv6.rs/tutorial/Ubuntu\_Server\_Latest/Kong/](https://ipv6.rs/tutorial/Ubuntu_Server_Latest/Kong/)

[https://documentation.wazuh.com/current/deployment-options/docker/wazuh-container.html](https://documentation.wazuh.com/current/deployment-options/docker/wazuh-container.html)
