Skip to main content
  1. Blog/

DLP e Wazuh

Alessio Barnini
Author
Alessio Barnini
Table of Contents

TL;DR
  • DLP fatto in casa: con 50 righe di Python e inotify monitoriamo in tempo reale la scrittura di file sensibili.
  • Pattern matching: uno switch match/case in Python 3.10+ intercetta SSN, carte di credito e codici fiscali.
  • Wazuh in Docker: integrazione con il manager tramite regole custom, superando i limiti di permessi e decodificatori.
  • Tre errori reali: come risolvere il blocco PEP 668 su Ubuntu 24.04, i permessi di docker cp e l'errore del decoder syslog.
$ history
  • python3 -m venv ~/dlp-venv
  • source ~/dlp-venv/bin/activate
  • pip install inotify
  • docker cp single-node-wazuh.manager-1:/var/ossec/etc/rules/local_rules.xml ./local_rules.xml
  • docker exec single-node-wazuh.manager-1 chown wazuh:wazuh /var/ossec/etc/rules/local_rules.xml
  • tail -f /var/log/syslog

Apro la dashboard Wazuh. Nella colonna full_log c'è scritto:

DLP_ALERT: SSN rilevato in test-data.txt SSN: 123-45-6789

Rule id 100200, level 10. Ha funzionato.

Alert generato su Wazuh

Facciamo un passo indietro.


Nelle aziende moderne, la fuga di dati sensibili (data exfiltration) è una delle minacce più concrete. I sistemi di DLP (Data Loss Prevention) enterprise fanno una cosa semplice alla base: analizzano i dati in tempo reale e bloccano il trasferimento non autorizzato. Ma come funzionano sotto il cofano?

Costruire un DLP minimale in Python ci permette di capire le dinamiche di monitoraggio dell'host (Endpoint DLP) e di integrazione con un SIEM senza perdersi in complesse installazioni enterprise.


Il contesto e l'architettura
#

La classificazione dei dati è il primo passo di qualsiasi strategia DLP. Non si può proteggere ciò che non si conosce: prima di applicare qualsiasi blocco, dobbiamo definire quali pattern rappresentano informazioni riservate o regolate, come le PII (Personally Identifiable Information). In questo laboratorio ci concentreremo su tre pattern comuni: codici fiscali italiani, numeri di previdenza sociale americani (SSN) e numeri di carte di credito.

Ecco come si sviluppa il flusso di monitoraggio e detection in questo scenario:

graph TD
    User[Utente o Processo] -->|Scrive file| Folder["Cartella Monitorata (~/dlp-watch)"]
    Folder -->|Notifica evento| Inotify["InotifyTree (Kernel Linux)"]
    Inotify -->|Rileva evento| PyScript["Script Python (dlp_monitor.py)"]
    PyScript -->|Lettura e Regex Match| Reg["Regex (SSN, Card, CF)"]
    Reg -->|Corrispondenza Trovata| Syslog["Syslog (/var/log/syslog)"]
    Syslog -->|Lettura Log| Agent["Wazuh Agent"]
    Agent -->|Inoltro Alert| Manager["Wazuh Manager (Docker)"]
    Manager -->|Genera Alert Livello 10| Dashboard["Wazuh Dashboard"]

Sistema di test: Linux (Ubuntu Server 24.04 su UTM) Requisiti installati: Python 3.10+, Wazuh Agent attivo, Wazuh Manager in container Docker (single-node-wazuh.manager-1).


Step 1 - Preparare l'ambiente e superare PEP 668
#

Dobbiamo installare le dipendenze Python necessarie per interfacciarsi con l'API inotify del kernel Linux e creare la cartella che simulerà la directory di lavoro degli utenti da monitorare.

Su Ubuntu 24.04, l'esecuzione diretta di pip install a livello globale viene bloccata da PEP 668 per proteggere l'ambiente di sistema. Creiamo quindi un ambiente virtuale (venv):

python3 -m venv ~/dlp-venv
source ~/dlp-venv/bin/activate
pip install inotify
mkdir -p ~/dlp-watch

Nelle chiamate:

  • Il modulo -m venv (dove -m indica a Python di eseguire il modulo venv come script) isola le dipendenze in una cartella locale.
  • Il flag -p (--parents) assicura che mkdir crei tutte le directory genitrici mancanti nel percorso fornito e che non fallisca se la cartella esiste già.

Una volta attivato il venv, nel prompt comparirà (dlp-venv) ad indicare l'isolamento attivo.


Step 2 - Lo script Python (Il motore DLP)
#

Creiamo lo script principale ~/dlp_monitor.py. Questo script rimarrà in ascolto degli eventi del filesystem sulla cartella /home/barno/dlp-watch.

Utilizziamo la classe InotifyTree della libreria inotify per agganciarci al kernel Linux e monitorare ricorsivamente la cartella, evitando il polling costante. Inotify ci avvisa degli eventi in tempo reale, dopodiché lo script esegue la scansione ed effettua il pattern matching tramite uno switch match/case (introdotto in Python 3.10+).

Ecco il codice completo dello script:

#!/usr/bin/env python3
import os
import inotify.adapters
import re
import syslog

def _main():
    folder = "/home/barno/dlp-watch"
    i = inotify.adapters.InotifyTree(folder)

    print(f"DLP_MONITOR in ascolto su {folder}...")

    for event in i.event_gen(yield_nones=False):
        (_, type_names, path, filename) = event
        filepath = os.path.join(path, filename)

        # Evita che lo script tenti di aprire directory
        if not os.path.isfile(filepath):
            continue

        try:
            with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:
                content = f.read()
        except Exception as e:
            syslog.syslog(syslog.LOG_ERR, f"Errore lettura file {filename}: {str(e)}")
            continue

        match_type = detect_pattern(content)
        if match_type:
            # Scrive l'evento nel log di sistema con la signature univoca DLP_ALERT
            syslog.syslog(f"DLP_ALERT: {match_type} rilevato in {filename} {content}")

def detect_pattern(content):
    match True:
        case _ if re.search(r'\b\d{3}-\d{2}-\d{4}\b', content):
            return 'SSN'
        case _ if re.search(r'\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b', content):
            return 'credit_card'
        case _ if re.search(r'\b[A-Z]{6}\d{2}[A-Z]\d{2}[A-Z]\d{3}[A-Z]\b', content):
            return 'codice fiscale'
        case _:
            return None

if __name__ == '__main__':
    _main()

InotifyTree monitora la cartella ricorsivamente. Ogni evento porta con sé path e filename, mentre la lettura del file viene delegata allo script. Il controllo os.path.isfile() evita crash derivanti dall'apertura di directory rilevate da inotify.


Step 3 - Testare il rilevamento locale
#

Attiviamo l'ambiente e avviamo lo script DLP nel primo terminale:

source ~/dlp-venv/bin/activate
python3 ~/dlp_monitor.py

Nel secondo terminale, simuliamo la creazione di un file con dati sensibili:

echo "SSN: 123-45-6789" > ~/dlp-watch/test-data.txt
echo "Card: 4111 1111 1111 1111" >> ~/dlp-watch/test-data.txt

Verifichiamo immediatamente i log di sistema syslog usando tail e filtrando per la nostra stringa:

sudo tail -f /var/log/syslog | grep DLP

Il flag -f (--follow) di tail indica di mantenere il file aperto e stampare in tempo reale i nuovi log accodati.

Nei log dovremmo vedere la segnalazione corretta:

# Output simulato
Jun  4 16:55:39 barno-server dlp-monitor.py[183638]: DLP_ALERT: SSN rilevato in test-data.txt SSN: 123-45-6789

Step 4 - Integrazione con Wazuh (Superando tre errori reali)
#

Dato che Wazuh Manager in questo laboratorio gira in un container Docker, l'integrazione ha richiesto la risoluzione di tre problemi pratici.

Errore 1 - Nessun editor nel container
#

Il file delle regole locali /var/ossec/etc/rules/local_rules.xml risiede all'interno del container Docker del manager, che non ha preinstallati editor di testo come vim o nano.

Workaround: Copiamo il file all'esterno, modifichiamolo sull'host e ricopiamolo dentro:

docker cp single-node-wazuh.manager-1:/var/ossec/etc/rules/local_rules.xml ./local_rules.xml
# Modifica il file locale con l'editor dell'host
docker cp ./local_rules.xml single-node-wazuh.manager-1:/var/ossec/etc/rules/local_rules.xml

Il comando docker cp copia file o directory tra il filesystem locale dell'host e un container in esecuzione.

Errore 2 - Permission denied
#

Dopo aver ricopiato il file, docker cp assegna la proprietà del file a root. Di conseguenza, Wazuh Manager (che gira con l'utente wazuh) non riesce a leggerlo all'avvio.

Dobbiamo riassegnare la proprietà dell'owner e del gruppo corretti all'interno del container:

docker exec single-node-wazuh.manager-1 chown wazuh:wazuh /var/ossec/etc/rules/local_rules.xml
docker exec single-node-wazuh.manager-1 chmod 660 /var/ossec/etc/rules/local_rules.xml

I comandi eseguiti tramite docker exec (che avvia un comando in un container attivo) modificano i permessi:

  • chown wazuh:wazuh imposta l'utente e il gruppo proprietari del file.
  • chmod 660 garantisce permessi di lettura e scrittura all'owner e al gruppo, negando qualsiasi accesso ad altri utenti.

Errore 3 - Decoder inesistente
#

Inizialmente, la regola configurata con il tag <decoded_as>syslog</decoded_as> impediva l'avvio di Wazuh, restituendo l'errore:

wazuh-analysisd: ERROR: Invalid decoder name: 'syslog'.

In Wazuh syslog non è un nome di decoder valido predefinito. Poiché la nostra stringa DLP_ALERT è univoca, possiamo omettere del tutto l'indicazione del decoder e fare affidamento sul blocco <match> per intercettare la signature.

Ecco la regola finale da inserire in local_rules.xml:

<group name="custom_rules_example,">
  <rule id="100200" level="10">
    <match>DLP_ALERT</match>
    <description>DLP: dato sensibile rilevato</description>
  </rule>
</group>

Applichiamo le modifiche riavviando il servizio all'interno del container:

docker exec single-node-wazuh.manager-1 /var/ossec/bin/wazuh-control restart

Step 5 - Rilevamento in Dashboard
#

Ora il flusso di rilevamento funziona end-to-end:

sequenceDiagram
    autonumber
    actor Utente as Utente / Attaccante
    participant FS as Cartella Monitorata
    participant Agent as DLP Monitor (Python)
    participant Syslog as Syslog (/var/log/syslog)
    participant Wazuh as Wazuh Agent
    participant Manager as Wazuh Manager (Docker)

    Utente->>FS: Scrive PII in un file
    FS-->>Agent: Notifica evento filesystem
    Agent->>FS: Scansiona il contenuto del file
    Note over Agent: Regex match positivo (match/case)
    Agent->>Syslog: Scrive DLP_ALERT con i dettagli
    Wazuh->>Syslog: Legge il log tramite journald
    Wazuh->>Manager: Invia l'evento
    Note over Manager: Regola 100200 applicata (livello 10)
    Manager-->>Manager: Mostra alert nella dashboard

Se apriamo la nostra Wazuh Dashboard, l'alert di livello 10 si attiva con Rule ID 100200.

Dettaglio dell'evento DLP con SSN rilevato

Wazuh compila autonomamente alcuni dettagli:

  • predecoder.program_name valorizzato a dlp-monitor.py (rilevato automaticamente dal syslog format).
  • location impostata a journald, dato che su Ubuntu 24.04 i log di syslog vengono catturati direttamente da journald prima dell'invio.

Varianti e casi limite
#

  • Performance con grandi volumi di dati: Lo script apre e legge in blocco ogni file modificato con f.read(). In ambienti di produzione con file di grandi dimensioni o continui accodamenti (es. scritture incrementali >>), questo approccio provocherebbe colli di bottiglia o duplicati di alert. È preferibile implementare una coda asincrona o un meccanismo di debounce per analizzare solo le modifiche nette e i file chiusi stabilmente.
  • Cifratura: Se un attaccante o un insider malevolo cifra i dati (ad esempio salvandoli in un archivio protetto da password) prima di spostarli nella cartella monitorata, le espressioni regolari non troveranno corrispondenze. L'esfiltrazione cifrata rimane il bypass più classico ed efficace contro un DLP a livello utente.

exit 0
#

Cinquanta righe di Python che fanno pattern matching su un filesystem event rappresentano il cuore logico di qualsiasi agente DLP per endpoint.

La vera differenza in produzione la fa la parte di risposta attiva: non limitarsi a segnalare il dato nei log, ma bloccare la scrittura, mettere in quarantena il file o sospendere temporaneamente la sessione dell'utente. Quello è il territorio di Wazuh active response, un ottimo punto di partenza per il prossimo laboratorio.


Comandi usati: tail · docker Concetti correlati: IDS/IPS · wazuh-architecture · wazuh-auditd-custom-rule

Related