feat: create n8n workflow for secure log ingestion
Implement LogWhisperer_Ingest workflow for Sprint 2 Feature 2: Workflow Components: - Webhook trigger: POST /webhook/logwhisperer/ingest - HMAC-SHA256 validation with timing-safe comparison - Anti-replay protection (5min timestamp window) - Data validation: UUID client_id, severity levels, non-empty logs - PostgreSQL storage with logs table auto-creation - Conditional routing for critical severity logs Security Features: - HMAC signature verification (X-LogWhisperer-Signature header) - Timestamp validation preventing replay attacks - Input sanitization before DB insert - Environment variable LOGWHISPERER_SECRET for shared secret Documentation: - workflows/logwhisperer_ingest.json: Export JSON workflow - workflows/README.md: Installation and usage guide - workflows/INTEGRATION.md: Bash script integration guide - workflows/REPORT.md: Implementation report - workflows/test_workflow.sh: Automated test suite Metodo Sacchi Applied: - Safety First: HMAC validation before any processing - Little Often: Modular nodes, each with single responsibility - Double Check: Test suite validates all security requirements Next Steps: - Configure LOGWHISPERER_SECRET in n8n environment - Import workflow to n8n instance - Test end-to-end with secure_logwhisperer.sh
This commit is contained in:
353
workflows/INTEGRATION.md
Normal file
353
workflows/INTEGRATION.md
Normal file
@@ -0,0 +1,353 @@
|
||||
# Guida Integrazione: Script Bash ↔ Workflow n8n
|
||||
|
||||
Questa guida descrive come integrare `secure_logwhisperer.sh` con il workflow n8n `LogWhisperer_Ingest`.
|
||||
|
||||
## 🔄 Flusso di Dati
|
||||
|
||||
```
|
||||
┌─────────────────┐ HMAC-SHA256 ┌──────────────────┐
|
||||
│ secure_logwhis- │ ───────────────────> │ Webhook n8n │
|
||||
│ perer.sh │ POST /ingest │ LogWhisperer_ │
|
||||
│ │ │ Ingest │
|
||||
└─────────────────┘ └──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ PostgreSQL │
|
||||
│ Table: logs │
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
## ⚙️ Configurazione
|
||||
|
||||
### 1. Configurare il Client (Script Bash)
|
||||
|
||||
Crea un file `config.env` nella directory del progetto:
|
||||
|
||||
```bash
|
||||
# config.env
|
||||
CLIENT_ID="550e8400-e29b-41d4-a716-446655440000"
|
||||
CLIENT_SECRET="your-secret-32-chars-long-minimum-here"
|
||||
WEBHOOK_URL="https://192.168.254.12:5678/webhook/logwhisperer/ingest"
|
||||
MAX_LINE_LENGTH=2000
|
||||
OFFSET_DIR="/var/lib/logwhisperer"
|
||||
```
|
||||
|
||||
**Requisiti:**
|
||||
- `CLIENT_ID`: UUID v4 valido
|
||||
- `CLIENT_SECRET`: Minimo 32 caratteri, no spazi
|
||||
- `WEBHOOK_URL`: Deve usare HTTPS in produzione
|
||||
|
||||
### 2. Configurare il Server (n8n)
|
||||
|
||||
#### Impostare Variabile Ambiente
|
||||
|
||||
**Docker Compose:**
|
||||
|
||||
```yaml
|
||||
services:
|
||||
n8n:
|
||||
image: n8nio/n8n
|
||||
environment:
|
||||
- LOGWHISPERER_SECRET=your-secret-32-chars-long-minimum-here
|
||||
- DB_TYPE=postgresdb
|
||||
- DB_POSTGRESDB_HOST=postgres
|
||||
- DB_POSTGRESDB_DATABASE=n8n
|
||||
- DB_POSTGRESDB_USER=n8n
|
||||
- DB_POSTGRESDB_PASSWORD=password
|
||||
```
|
||||
|
||||
**Docker Run:**
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name n8n \
|
||||
-p 5678:5678 \
|
||||
-e LOGWHISPERER_SECRET="your-secret-32-chars-long-minimum-here" \
|
||||
-v ~/.n8n:/home/node/.n8n \
|
||||
n8nio/n8n
|
||||
```
|
||||
|
||||
#### Configurare Credenziali PostgreSQL
|
||||
|
||||
1. Accedi a n8n UI: http://192.168.254.12:5678
|
||||
2. Vai su **Settings** → **Credentials**
|
||||
3. Clicca **Add Credential**
|
||||
4. Seleziona **PostgreSQL**
|
||||
5. Configura:
|
||||
- **Name**: `PostgreSQL LogWhisperer`
|
||||
- **Host**: `postgres` (o IP del tuo DB)
|
||||
- **Port**: `5432`
|
||||
- **Database**: `logwhisperer`
|
||||
- **User**: `logwhisperer`
|
||||
- **Password**: `your-password`
|
||||
- **SSL**: `disable` (per test locale)
|
||||
|
||||
### 3. Verificare Segreto Condiviso
|
||||
|
||||
Il segreto DEVE essere identico su client e server:
|
||||
|
||||
**Client (Bash):**
|
||||
```bash
|
||||
# Genera firma di test
|
||||
./scripts/secure_logwhisperer.sh --generate-hmac '{"test":"data"}' 'test-secret-32-chars-long-minimum' 1234567890
|
||||
# Output: 1234567890:abc123...
|
||||
```
|
||||
|
||||
**Server (n8n Code Node):**
|
||||
|
||||
Il nodo `HMAC Validation` calcola la firma con lo stesso algoritmo:
|
||||
|
||||
```javascript
|
||||
const expectedSignature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(`${timestamp}:${payload}`)
|
||||
.digest('hex');
|
||||
```
|
||||
|
||||
## 🚀 Esempio di Uso Completo
|
||||
|
||||
### Step 1: Validare Configurazione
|
||||
|
||||
```bash
|
||||
cd /home/google/Sources/LucaSacchiNet/LogWhispererAI
|
||||
|
||||
# Verifica dipendenze
|
||||
./scripts/secure_logwhisperer.sh --check-deps
|
||||
|
||||
# Valida configurazione
|
||||
./scripts/secure_logwhisperer.sh --validate-config
|
||||
```
|
||||
|
||||
### Step 2: Test Ingezione Singola
|
||||
|
||||
```bash
|
||||
# Sanitizza una linea di log
|
||||
SANITIZED=$(./scripts/secure_logwhisperer.sh --sanitize-line "Apr 2 10:30:00 kernel: password=secret123 Out of memory")
|
||||
echo "$SANITIZED"
|
||||
# Output: Apr 2 10:30:00 kernel: password=*** Out of memory
|
||||
|
||||
# Genera payload JSON
|
||||
PAYLOAD=$(./scripts/secure_logwhisperer.sh --encode-json '{
|
||||
"client_id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"hostname": "web-server-01",
|
||||
"source": "/var/log/syslog",
|
||||
"severity": "critical",
|
||||
"raw_log": "Apr 2 10:30:00 kernel: Out of memory",
|
||||
"matched_pattern": "OOM"
|
||||
}')
|
||||
|
||||
# Genera firma HMAC
|
||||
TIMESTAMP=$(date +%s)
|
||||
SIGNATURE=$(./scripts/secure_logwhisperer.sh --generate-hmac "$PAYLOAD" "$CLIENT_SECRET" "$TIMESTAMP")
|
||||
|
||||
# Invia a n8n
|
||||
curl -X POST "$WEBHOOK_URL" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "X-LogWhisperer-Signature: $SIGNATURE" \
|
||||
-H "X-LogWhisperer-Timestamp: $TIMESTAMP" \
|
||||
-d "$PAYLOAD"
|
||||
```
|
||||
|
||||
### Step 3: Verifica Salvataggio
|
||||
|
||||
```bash
|
||||
# Connettiti al database PostgreSQL
|
||||
psql -h localhost -U logwhisperer -d logwhisperer
|
||||
|
||||
# Query per verificare inserimento
|
||||
SELECT * FROM logs ORDER BY created_at DESC LIMIT 5;
|
||||
|
||||
# Esci
|
||||
\q
|
||||
```
|
||||
|
||||
## 🔐 Sicurezza End-to-End
|
||||
|
||||
### HMAC Signature Format
|
||||
|
||||
```
|
||||
Header: X-LogWhisperer-Signature: <timestamp>:<signature>
|
||||
Header: X-LogWhisperer-Timestamp: <timestamp>
|
||||
|
||||
Dove:
|
||||
- timestamp: Unix epoch seconds
|
||||
- signature: HMAC-SHA256(timestamp:payload, secret)
|
||||
```
|
||||
|
||||
### Esempio Calcolo HMAC (Bash)
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
payload='{"client_id":"550e8400-e29b-41d4-a716-446655440000","severity":"critical","raw_log":"test"}'
|
||||
timestamp=$(date +%s)
|
||||
secret="test-secret-32-chars-long-minimum"
|
||||
|
||||
# Calcola HMAC
|
||||
signature=$(printf '%s:%s' "$timestamp" "$payload" | \
|
||||
openssl dgst -sha256 -hmac "$secret" | \
|
||||
sed 's/^.* //')
|
||||
|
||||
echo "Timestamp: $timestamp"
|
||||
echo "Signature: $signature"
|
||||
echo "Full: ${timestamp}:${signature}"
|
||||
```
|
||||
|
||||
### Esempio Calcolo HMAC (JavaScript/n8n)
|
||||
|
||||
```javascript
|
||||
const crypto = require('crypto');
|
||||
|
||||
const payload = '{"client_id":"550e8400-e29b-41d4-a716-446655440000","severity":"critical","raw_log":"test"}';
|
||||
const timestamp = Math.floor(Date.now() / 1000);
|
||||
const secret = "test-secret-32-chars-long-minimum";
|
||||
|
||||
const signature = crypto
|
||||
.createHmac('sha256', secret)
|
||||
.update(`${timestamp}:${payload}`)
|
||||
.digest('hex');
|
||||
|
||||
console.log(`Timestamp: ${timestamp}`);
|
||||
console.log(`Signature: ${signature}`);
|
||||
console.log(`Full: ${timestamp}:${signature}`);
|
||||
```
|
||||
|
||||
## 🧪 Test di Integrazione
|
||||
|
||||
### Test 1: Validazione Completa
|
||||
|
||||
```bash
|
||||
./workflows/test_workflow.sh
|
||||
```
|
||||
|
||||
### Test 2: Flusso Completo
|
||||
|
||||
```bash
|
||||
# 1. Crea una linea di log di test
|
||||
echo "Apr 2 12:00:00 kernel: FATAL: Out of memory: Kill process 1234" > /tmp/test_critical.log
|
||||
|
||||
# 2. Processa con secure_logwhisperer.sh
|
||||
# (Assumendo che lo script legga da file e invii a webhook)
|
||||
# TODO: Implementare modalità daemon nel prossimo sprint
|
||||
|
||||
# 3. Verifica in database
|
||||
psql -h localhost -U logwhisperer -c "SELECT * FROM logs WHERE severity='critical' ORDER BY created_at DESC LIMIT 1;"
|
||||
```
|
||||
|
||||
## 📊 Monitoraggio
|
||||
|
||||
### Log n8n
|
||||
|
||||
```bash
|
||||
# Visualizza log in tempo reale
|
||||
docker logs -f n8n
|
||||
|
||||
# Cerca errori specifici
|
||||
docker logs n8n 2>&1 | grep -i "logwhisperer\|error\|unauthorized"
|
||||
```
|
||||
|
||||
### Metriche
|
||||
|
||||
- **Richieste totali**: Conteggio righe in tabella `logs`
|
||||
- **Errori 401**: Webhook chiamate rifiutate (HMAC invalido)
|
||||
- **Errori 400**: Validazione dati fallita
|
||||
- **Latency**: Tempo medio di risposta del webhook
|
||||
|
||||
## 🐛 Troubleshooting Comuni
|
||||
|
||||
### "Invalid signature" (401)
|
||||
|
||||
**Causa**: Segreti diversi tra client e server
|
||||
|
||||
**Soluzione**:
|
||||
```bash
|
||||
# Verifica segreto sul client
|
||||
echo "CLIENT_SECRET: $CLIENT_SECRET"
|
||||
|
||||
# Verifica segreto sul server (n8n container)
|
||||
docker exec n8n echo "$LOGWHISPERER_SECRET"
|
||||
|
||||
# Devono essere identici!
|
||||
```
|
||||
|
||||
### "Request timestamp too old" (401)
|
||||
|
||||
**Causa**: Clock skew tra client e server
|
||||
|
||||
**Soluzione**:
|
||||
```bash
|
||||
# Sincronizza orario
|
||||
sudo ntpdate pool.ntp.org
|
||||
|
||||
# O su container n8n
|
||||
docker exec n8n date
|
||||
docker exec n8n sh -c "date -s '@$(date +%s)'"
|
||||
```
|
||||
|
||||
### Database connection error
|
||||
|
||||
**Causa**: Credenziali PostgreSQL errate o database non raggiungibile
|
||||
|
||||
**Soluzione**:
|
||||
```bash
|
||||
# Test connessione
|
||||
docker exec n8n pg_isready -h postgres -p 5432
|
||||
|
||||
# Verifica credenziali
|
||||
docker exec n8n psql -h postgres -U logwhisperer -d logwhisperer -c "SELECT 1;"
|
||||
```
|
||||
|
||||
## 🔄 Workflow CI/CD
|
||||
|
||||
Per test automatici in CI/CD:
|
||||
|
||||
```yaml
|
||||
# .github/workflows/integration-test.yml
|
||||
name: Integration Tests
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:15
|
||||
env:
|
||||
POSTGRES_DB: logwhisperer
|
||||
POSTGRES_USER: logwhisperer
|
||||
POSTGRES_PASSWORD: test
|
||||
ports:
|
||||
- 5432:5432
|
||||
n8n:
|
||||
image: n8nio/n8n
|
||||
env:
|
||||
LOGWHISPERER_SECRET: test-secret-32-chars-long-minimum
|
||||
ports:
|
||||
- 5678:5678
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Import Workflow
|
||||
run: |
|
||||
curl -X POST http://localhost:5678/api/v1/workflows \
|
||||
-H "Content-Type: application/json" \
|
||||
-d @workflows/logwhisperer_ingest.json
|
||||
|
||||
- name: Run Tests
|
||||
run: ./workflows/test_workflow.sh
|
||||
```
|
||||
|
||||
## 📝 Checklist Pre-Deploy
|
||||
|
||||
- [ ] `CLIENT_SECRET` configurato su client (min 32 chars)
|
||||
- [ ] `LOGWHISPERER_SECRET` configurato su server (identico al client)
|
||||
- [ ] Credenziali PostgreSQL configurate in n8n
|
||||
- [ ] Workflow importato e attivato
|
||||
- [ ] Tabella `logs` creata (automatizzato dal workflow)
|
||||
- [ ] Test suite passati (`./workflows/test_workflow.sh`)
|
||||
- [ ] HTTPS abilitato (in produzione)
|
||||
- [ ] Rate limiting configurato
|
||||
- [ ] Monitoring e alerting attivo
|
||||
Reference in New Issue
Block a user