--- # 🧠 MEMORY.md ```markdown # 🧠 MEMORY.md - Knowledge Base Progetto NetConfig Plugin > **Scopo**: Memoria persistente delle decisioni, contesto e apprendimenti per il team di sviluppo > **Progetto**: GLPI 11 Plugin - Network Configuration Backup & Versioning > **Stato**: ✅ Specifica Completa | 🚧 Implementazione Pronta | 🧪 Test in Corso --- ## 📋 Riepilogo Richieste Utente ### Richiesta Originale (Tradotta e Strutturata) ``` "Vorrei implementare un plugin per GLPI 11 secondo le linee guida degli sviluppatori GLPI per: 1. Salvare configurazioni di apparati di rete (es. switch) nel database MySQL 2. Tenere traccia delle variazioni della configurazione (versioning) 3. Implementare massive actions (azioni di massa) 4. Gestire i profili come già integrato in GLPI (ACL native) 5. Permettere l'esportazione in file di testo del contenuto salvato (solo per utenti con diritti) 6. Lanciare il comando per recuperare la configurazione dall'apparato di rete 7. Valutare l'utilizzo di un agent GLPI remoto per eseguire il recupero configurazione 8. Fornire una proposta architetturale completa" ``` ### Vincoli Espliciti - ✅ GLPI 11.0.6+ (verificato con `php bin/console glpi:version`) - ✅ PHP 8.2+ con strict_types e namespace PSR-4 - ✅ MySQL/MariaDB con charset utf8mb4 - ✅ Integrazione con sistema profili/diritti nativo GLPI - ✅ Massive actions compatibili con UI GLPI - ✅ Cifratura configurazioni sensibili - ✅ Agent remoto opzionale ma preferito per scalabilità --- ## 🗂️ Decisioni Architetturali Confermate ### ✅ Scelte Definitive | Area | Decisione | Motivazione | |------|-----------|-------------| | **Struttura Plugin** | PSR-4 autoload, `src/` per classi, `ajax/` per endpoint | Conformità linee guida GLPI 11, manutenibilità | | **Database** | Tabella dedicata `glpi_plugin_netconfig_configs` con MEDIUMTEXT | Isolamento dati, performance query, supporto config grandi | | **Versioning** | SHA256 hash + confronto all'arrivo nuovo backup | Rilevamento modifiche efficiente, storage ottimizzato | | **Cifratura** | `Glpi\Toolbox\Encryption` (AES-256-GCM) | Integrazione nativa, chiave gestita da GLPI, compliance | | **Agent Remoto** | Python + netmiko + YAML config + systemd timer | Supporto multi-vendor maturo, gestione timeout/retry robusta | | **Diff Visual** | `symfony/diff` con output HTML sanitizzato per Twig | Libreria mantenuta, output sicuro, integrazione semplice | | **Export** | Stream diretto con redaction regex + controllo diritti | Performance, sicurezza, compliance policy aziendali | | **API Endpoint** | POST JSON con token + validazione hash lato server | Stateless, compatibile con agent distribuiti, auditabile | ### ❌ Alternative Scartate (con Motivazione) | Alternativa | Motivazione Scarto | |-------------|-------------------| | SSH diretto da PHP (phpseclib) | Timeout frequenti, gestione connessioni concorrenti complessa, scaling limitato | | Archiviazione su filesystem | Perdita integrazione ACL GLPI, backup/disaster recovery più complesso, audit difficile | | Integrazione Oxidized/RANCID | Overhead infrastrutturale aggiuntivo, duplicazione logica versioning, curva apprendimento team | | Agent in PHP invece di Python | Netmiko/napalm hanno supporto multi-vendor più maturo e community attiva in Python | | Diff calcolato lato client (JS) | Configurazioni grandi (>1MB) causano lag browser, sanitizzazione complessa per XSS | --- ## 🔐 Security & Compliance Decisions ### Credenziali e Segreti ```yaml # ✅ DO: Usare variabili d'ambiente o vault esterno NETCONFIG_DEFAULT_USER: "backup_svc" NETCONFIG_DEFAULT_PASS: "${VAULT_SECRET_NETCONFIG_PASS}" NETCONFIG_AGENT_TOKEN: "${OPENSSL_GENERATED_32B_HEX}" # ❌ DON'T: Hardcodare nel codice o YAML # password: "SuperSecret123" # MAI fare questo ``` ### Cifratura Dati - **A riposo**: `config_content` cifrato con `Glpi\Toolbox\Encryption::encrypt()` - **Chiave**: Derivata da `crypt_key` in `GLPI_CONFIG_DIR/config_db.php` (gestita da GLPI) - **Algoritmo**: AES-256-GCM con autenticazione (previene tampering) - **Verifica**: Test manuale post-install per confermare che DB contenga blob cifrati ### Controllo Accessi ```php // Pattern confermato per tutti i metodi sensibili if (!\PluginNetconfig\Config::canView()) { http_response_code(403); exit('Access denied'); } ``` ### Redaction Export ```php // Regex per mascherare credenziali prima dell'export $content = preg_replace( '/(?<=password\s|secret\s|enable\s|community\s)[^\s\r\n]+/i', '***REDACTED***', $content ); // Personalizzabile in base alle policy aziendali ``` --- ## 🧪 Test Plan Confermato ### Test Unitari (PHPUnit - da implementare) ```php // Esempio: test hash detection public function testSaveConfig_DetectsNoChanges(): void { $config = new \PluginNetconfig\Config(); $result = $config->saveConfig([ 'networkdevices_id' => 123, 'content' => 'identical config content' ]); // Primo salvataggio: true // Secondo salvataggio stesso contenuto: true ma nessun nuovo record } ``` ### Test di Integrazione 1. **Installazione plugin**: Verificare creazione tabella e registrazione hook 2. **Diritti profilo**: Assegnare READ a utente test, verificare accesso a tab Config History 3. **Agent simulation**: Invio JSON via curl, verificare risposta e record in DB 4. **Export flow**: Login utente con diritti, click export, verificare download e redaction 5. **Diff visual**: Salvare due versioni diverse, verificare rendering HTML differenze ### Test di Sicurezza ```bash # 1. Tentativo export senza diritti curl -b "glpi_session=invalid" http://glpi/plugins/netconfig/ajax/export.php?id=1 # Atteso: HTTP 403 # 2. Invio agent con token errato curl -X POST ... -d '{"token":"wrong"}' # Atteso: {"status":"error","message":"Unauthorized"} # 3. Verifica cifratura DB mysql -e "SELECT config_content FROM glpi_plugin_netconfig_configs LIMIT 1" | head # Atteso: blob binario non leggibile, non testo in chiaro ``` --- ## 📦 Dipendenze e Versioni Confermate ### composer.json (Definitivo) ```json { "name": "glpi-plugin/netconfig", "license": "GPL-3.0-or-later", "minimum-stability": "stable", "prefer-stable": true, "require": { "php": ">=8.2", "symfony/diff": "^6.4", "guzzlehttp/guzzle": "^7.8" }, "autoload": { "psr-4": { "PluginNetconfig\\": "src/", "PluginNetconfig\\Api\\": "src/Api/" } } } ``` ### Requisiti Python Agent ```txt # requirements-agent.txt netmiko>=4.3.0 requests>=2.31.0 pyyaml>=6.0.1 cryptography>=41.0.0 # Per eventuale cifratura lato agent (opzionale) ``` ### Estensioni PHP Richieste ```bash php -m | grep -E "curl|json|mbstring|openssl|zip|pdo_mysql" # Tutte devono essere presenti per GLPI 11 + plugin ``` --- ## 🔄 Flussi Operativi Documentati ### Flusso 1: Backup Schedulato (Agent Remoto) ```mermaid sequenceDiagram participant Cron as Systemd Timer/Cron participant Agent as GLPI Agent (Python) participant Device as Network Device participant GLPI as GLPI Server + Plugin Cron->>Agent: Trigger ogni 6h Agent->>Device: SSH via netmiko (show running-config) Device-->>Agent: Restituisce configurazione Agent->>Agent: Calcola SHA256, confronta con ultimo locale (opzionale) Agent->>GLPI: POST /ajax/agent_receive.php con JSON GLPI->>GLPI: Valida token, decifra chiave, confronta hash DB alt Hash diverso GLPI->>GLPI: Cifra contenuto, salva nuovo record, registra versione GLPI-->>Agent: {"status":"ok","message":"Saved"} else Hash identico GLPI-->>Agent: {"status":"ok","message":"No changes"} end Agent->>Cron: Log risultato, exit ``` ### Flusso 2: Export Configurazione (Utente GLPI) ```mermaid sequenceDiagram participant User as Utente GLPI participant UI as Interfaccia GLPI participant Plugin as Plugin NetConfig participant DB as Database MySQL User->>UI: Clicca "Export" su configurazione UI->>Plugin: GET /ajax/export.php?id=XXX Plugin->>Plugin: Verifica Session::haveRight(Config::READ) alt Diritti insufficienti Plugin-->>UI: HTTP 403 + messaggio errore else Diritti OK Plugin->>DB: SELECT config_content, is_encrypted WHERE id=XXX DB-->>Plugin: Restituisce record Plugin->>Plugin: Decifra se is_encrypted=1 Plugin->>Plugin: Applica regex redaction su password/secret Plugin-->>User: HTTP 200 + file .txt in download end ``` ### Flusso 3: Visualizzazione Diff (UI GLPI) ```mermaid sequenceDiagram participant User as Utente GLPI participant Twig as Template Engine participant Plugin as Plugin NetConfig participant DiffLib as symfony/diff User->>UI: Apri tab "Config History" su NetworkEquipment UI->>Plugin: Richiedi liste configurazioni per device_id Plugin->>DB: SELECT * ORDER BY created_at DESC DB-->>Plugin: Restituisce array configurazioni Plugin->>Plugin: Per l'ultima config, carica precedente e calcola diff Plugin->>DiffLib: Differ::diff(old, new) con HtmlOutput DiffLib-->>Plugin: Stringa HTML con / Plugin->>Twig: Passa configs + diff_html al template Twig->>User: Renderizza tabella con pulsante "View Diff" User->>UI: Clicca pulsante diff UI->>User: Modal dialog con differenze evidenziate (verde/rosso) ``` --- ## 🚨 Lessons Learned & Pitfalls Evitati ### ✅ Cosa Abbiamo Imparato 1. **Composer in ambienti non-standard**: Il warning `installed.json not found` è innocuo se `composer install` completa con successo. Non bloccare l'installazione per questo. 2. **Hash vs Timestamp per versioning**: Confrontare hash SHA256 è più affidabile dei timestamp (orologi non sincronizzati, config identiche con timestamp diversi). 3. **Cifratura nativa GLPI**: Usare `Glpi\Toolbox\Encryption` evita di gestire chiavi separatamente e garantisce compatibilità con backup/restore GLPI. 4. **Diff on-demand**: Calcolare il diff solo per l'ultima configurazione (non per tutto lo storico) migliora le performance con config grandi. 5. **Agent token rotation**: Documentare la procedura di rotazione token nel README operativo, anche se non implementata nel codice. ### ⚠️ Pitfalls Evitati | Problema Potenziale | Come Lo Abbiamo Evitato | |---------------------|------------------------| | XSS nel diff HTML | Output di `symfony/diff` sanitizzato con `|escape('js')` in Twig | | SQL injection in massive actions | Uso di prepared statements in `getLastConfigByDevice()` | | Credential leak nei log | Logging configurato per escludere payload config, solo metadata | | Timeout SSH su dispositivi lenti | Retry logic con backoff + `global_delay_factor` configurabile per device | | Memory exhaustion con config grandi | Stream export invece di caricamento intero in memoria, uso di `MEDIUMTEXT` | --- ## 📞 Contatti e Responsabilità | Ruolo | Responsabilità | Contatto (Esempio) | |-------|---------------|-------------------| | **Plugin Maintainer** | Release, compatibilità GLPI, security patch | team-dev@azienda.it | | **Agent Operator** | Deploy script Python, gestione dispositivi YAML, monitoraggio | netops@azienda.it | | **Security Officer** | Approvazione policy redaction, rotazione token, audit cifratura | security@azienda.it | | **GLPI Admin** | Installazione plugin, gestione profili, backup database | glpi-admin@azienda.it | --- ## 🗓️ Cronologia Decisioni | Data | Decisione | Autore | Riferimento | |------|-----------|--------|-------------| | 2026-05-22 | Architettura plugin confermata (PSR-4, encryption nativa, agent Python) | AI Assistant + Utente | Richiesta iniziale | | 2026-05-22 | Scelta symfony/diff ^6.4 per visualizzazione differenze | AI Assistant | Valutazione librerie | | 2026-05-22 | Token-based auth per endpoint agent invece di session GLPI | AI Assistant | Requisito agent remoto | | 2026-05-22 | Redaction automatica password in export tramite regex | AI Assistant | Compliance sicurezza | | 2026-05-22 | Supporto massive actions native invece di UI custom | AI Assistant | Integrazione UX GLPI | --- ## 🔗 Link Utili per il Team - [Repository Plugin (interno)](https://git.azienda.it/glpi-plugins/netconfig) - [Documentazione GLPI 11 Developer](https://glpi-developer-documentation.readthedocs.io/en/11.0/) - [Netmiko Platform Support](https://github.com/ktbyers/netmiko/blob/develop/PLATFORMS.md) - [GLPI API Postman Collection](https://github.com/glpi-project/glpi/blob/11.0/apirest.md) - [Checklist Sicurezza Plugin GLPI](https://github.com/glpi-project/glpi/blob/11.0/DEV/SECURITY.md) --- > 📌 **Nota Operativa**: Questo documento va aggiornato ad ogni modifica architetturale significativa. > **Ultima revisione**: 2026-05-22 | **Prossima review pianificata**: 2026-08-22 ``` ---