Flashare Raspberry Pi OS sul SSD
Operazione da eseguire su un PC, prima di accendere il Raspberry
Scarica Raspberry Pi Imager da https://www.raspberrypi.com/software/ e installalo sul tuo PC Windows/Mac/Linux.
Collega il SSD NVMe al PC tramite un adattatore USB-to-M.2 e segui questi passi nell'Imager:
- Seleziona dispositivo: Raspberry Pi 5
- Seleziona OS: Raspberry Pi OS (other) → Raspberry Pi OS Lite (64-bit) — senza desktop, più leggero per un server
- Seleziona storage: il tuo SSD NVMe
- Clicca l'icona ⚙️ Impostazioni avanzate prima di scrivere
raspberry-lab), utente (es. pi), password sicura, SSH abilitato, WiFi se necessario. In questo modo il Raspberry si avvia già configurato senza bisogno di monitor/tastiera.Dopo aver scritto l'immagine, inserisci il SSD nel Raspberry Pi 5 tramite l'HAT+ NVMe ufficiale.
Primo accesso via SSH
Collegarsi al Raspberry dalla rete senza monitor né tastiera
Accendi il Raspberry Pi 5 e attendi 60 secondi per il boot completo. Poi trova l'IP assegnato dal router:
# Scansiona la rete per trovare il Raspberry (sostituisci 192.168.1 con la tua subnet) nmap -sn 192.168.1.0/24 | grep -A1 "Raspberry" # Oppure, se conosci il suo hostname: ping raspberry-lab.local # Una volta trovato l'IP, connettiti via SSH: ssh pi@192.168.1.X # Windows: usa PuTTY oppure il terminale di Windows 11 # Inserisci la password che hai scelto durante la configurazione
pi@raspberry-lab:~$ sei connesso correttamente.Aggiornare completamente il sistema
Fondamentale prima di qualsiasi installazione
# Aggiorna la lista dei pacchetti disponibili dai repository sudo apt update # Aggiorna tutti i pacchetti installati all'ultima versione disponibile sudo apt upgrade -y # Aggiorna anche i pacchetti del firmware e del kernel sudo apt full-upgrade -y # Rimuovi pacchetti obsoleti non più necessari sudo apt autoremove -y # Pulisci la cache locale dei pacchetti scaricati sudo apt autoclean # Riavvia il sistema (necessario dopo aggiornamenti del kernel) sudo reboot
Attendi il riavvio (circa 30 secondi) e riconnettiti via SSH.
Impostare un IP statico sulla rete scolastica
Indispensabile: il server deve avere sempre lo stesso IP
Se il Raspberry cambia IP ad ogni avvio, tutti i link e le configurazioni smetteranno di funzionare. Imposta un IP fisso:
# Mostra le connessioni di rete attive e il nome dell'interfaccia nmcli connection show # Di solito la connessione ethernet si chiama "Wired connection 1" # Imposta IP statico (adatta i valori alla tua rete scolastica) sudo nmcli connection modify "Wired connection 1" \ ipv4.addresses 192.168.1.200/24 \ ipv4.gateway 192.168.1.1 \ ipv4.dns "8.8.8.8,8.8.4.4" \ ipv4.method manual # Applica le modifiche riavviando la connessione sudo nmcli connection down "Wired connection 1" sudo nmcli connection up "Wired connection 1" # Verifica il nuovo IP assegnato ip addr show eth0
Installare i pacchetti di base necessari
Tool essenziali per tutti i progetti
# Installa tutti i tool essenziali in una sola riga sudo apt install -y \ curl wget git nano htop \ net-tools nmap ufw fail2ban \ python3 python3-pip python3-venv \ ca-certificates gnupg lsb-release \ unzip zip tree jq # Verifica le versioni installate python3 --version # es: Python 3.11.x git --version # es: git version 2.39.x curl --version # es: curl 7.88.x
Configurare il firewall UFW
Sicurezza base: blocca tutto tranne ciò che serve
# Imposta la policy di default: blocca tutto il traffico in entrata sudo ufw default deny incoming sudo ufw default allow outgoing # Permetti SSH (porta 22) — FONDAMENTALE: fallo PRIMA di attivare UFW sudo ufw allow 22/tcp # Permetti HTTP e HTTPS sudo ufw allow 80/tcp sudo ufw allow 443/tcp # Attiva il firewall (rispondo "y" alla conferma) sudo ufw enable # Controlla lo stato e le regole attive sudo ufw status verbose
Quando installi nuovi servizi, ricorda di aprire la porta corrispondente con sudo ufw allow PORTA/tcp.
- Capire la differenza tra cloud pubblico (Google Drive) e cloud privato self-hosted
- Comprendere l'architettura a layer: proxy → applicazione → database → storage
- Gestire utenti, gruppi e permessi su un server Linux reale
- Imparare i concetti fondamentali di Docker: immagini, container, volumi, reti
- Configurare un reverse proxy Nginx per esporre servizi web
Installare Docker Engine e Docker Compose
La piattaforma di containerizzazione su cui girerà Nextcloud
Docker permette di isolare ogni applicazione in un "container" con tutte le sue dipendenze. Installiamo la versione ufficiale (non quella dei repository Debian che è più vecchia):
# Rimuovi eventuali versioni vecchie di Docker sudo apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null # Aggiungi la chiave GPG ufficiale di Docker sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | \ sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod a+r /etc/apt/keyrings/docker.gpg # Aggiungi il repository Docker ai sorgenti apt echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null # Aggiorna i repository e installa Docker sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin # Aggiungi l'utente corrente al gruppo docker (evita sudo ogni volta) sudo usermod -aG docker $USER # Applica il cambio di gruppo SUBITO (senza riavviare la sessione SSH) newgrp docker # Verifica le versioni installate docker --version docker compose version # Verifica che il servizio Docker sia attivo sudo systemctl status docker
docker run hello-world — deve stampare "Hello from Docker!" senza errori.Preparare le directory e i file di configurazione
Creare la struttura di cartelle per Nextcloud con dati persistenti
# Crea la cartella principale del progetto mkdir -p ~/nextcloud cd ~/nextcloud # Crea le directory che verranno montate come volumi Docker mkdir -p data/nextcloud # file di Nextcloud (config, app) mkdir -p data/mariadb # database MariaDB mkdir -p data/userfiles # file caricati dagli utenti # Crea il file per le variabili d'ambiente (password separate dal compose) nano .env
Inserisci nel file .env queste variabili (modifica le password!):
# === PASSWORD DATABASE === MYSQL_ROOT_PASSWORD=RootPassSicura2024! MYSQL_DATABASE=nextcloud MYSQL_USER=nextcloud_user MYSQL_PASSWORD=NcDbPass2024! # === ADMIN NEXTCLOUD === NEXTCLOUD_ADMIN_USER=admin NEXTCLOUD_ADMIN_PASSWORD=AdminPass2024! # === DOMINIO (usa l'IP del Raspberry se non hai un dominio) NEXTCLOUD_TRUSTED_DOMAINS=192.168.1.200
.env contiene password. Non condividerlo mai. Aggiungilo a .gitignore se usi Git.Creare il file docker-compose.yml
Il file che descrive tutta l'architettura: Nextcloud + MariaDB + Redis
nano ~/nextcloud/docker-compose.yml
version: '3.8' services: # === DATABASE MariaDB === db: image: mariadb:10.11 container_name: nextcloud-db restart: always command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW environment: MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD} MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} volumes: # I dati del DB vengono salvati qui sul SSD (persistono anche se il container si riavvia) - ./data/mariadb:/var/lib/mysql networks: - nextcloud-net healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] interval: 10s timeout: 5s retries: 5 # === CACHE REDIS === redis: image: redis:7-alpine container_name: nextcloud-redis restart: always command: redis-server --requirepass nextcloudredis123 networks: - nextcloud-net # === APPLICAZIONE NEXTCLOUD === nextcloud: image: nextcloud:28-apache container_name: nextcloud-app restart: always ports: - "8080:80" # espone Nextcloud sulla porta 8080 del Raspberry environment: MYSQL_HOST: db MYSQL_DATABASE: ${MYSQL_DATABASE} MYSQL_USER: ${MYSQL_USER} MYSQL_PASSWORD: ${MYSQL_PASSWORD} REDIS_HOST: redis REDIS_HOST_PASSWORD: nextcloudredis123 NEXTCLOUD_ADMIN_USER: ${NEXTCLOUD_ADMIN_USER} NEXTCLOUD_ADMIN_PASSWORD: ${NEXTCLOUD_ADMIN_PASSWORD} NEXTCLOUD_TRUSTED_DOMAINS: ${NEXTCLOUD_TRUSTED_DOMAINS} PHP_MEMORY_LIMIT: 512M PHP_UPLOAD_LIMIT: 2G volumes: - ./data/nextcloud:/var/www/html # core di Nextcloud - ./data/userfiles:/var/www/html/data # file degli utenti networks: - nextcloud-net depends_on: db: condition: service_healthy redis: condition: service_started networks: nextcloud-net: driver: bridge
Avviare i container e verificare
Primo avvio: Docker scaricherà le immagini (~600MB totali)
# Vai nella cartella del progetto cd ~/nextcloud # Avvia tutto in background (-d = detached mode) docker compose up -d # Osserva i log in tempo reale (Ctrl+C per uscire senza fermare i container) docker compose logs -f # Controlla che tutti e 3 i container siano "Up" docker compose ps # Controlla lo stato dettagliato docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
Il primo avvio richiede 2-5 minuti mentre Nextcloud inizializza il database. Poi apri il browser:
http://192.168.1.200:8080 — Effettua login con le credenziali admin definite nel file .envSe la pagina non si apre, controlla i log con docker compose logs nextcloud per vedere gli errori.
Configurare Nginx come Reverse Proxy (porta 80)
Per accedere senza specificare la porta :8080
# Installa Nginx sudo apt install -y nginx # Crea il file di configurazione per Nextcloud sudo nano /etc/nginx/sites-available/nextcloud
server {
listen 80;
server_name 192.168.1.200; # o il tuo dominio
# Aumenta la dimensione massima dei file caricabili
client_max_body_size 2G;
# Header di sicurezza
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Timeout per upload file grandi
proxy_connect_timeout 300;
proxy_send_timeout 300;
proxy_read_timeout 300;
# Disabilita il buffering per i download
proxy_buffering off;
}
}
# Abilita il sito creando un link simbolico sudo ln -s /etc/nginx/sites-available/nextcloud /etc/nginx/sites-enabled/ # Rimuovi il sito di default di Nginx sudo rm -f /etc/nginx/sites-enabled/default # Testa la configurazione (deve rispondere "syntax is ok") sudo nginx -t # Applica la configurazione sudo systemctl reload nginx # Apri la porta 80 nel firewall sudo ufw allow 80/tcp
http://192.168.1.200 senza specificare la porta!Creare account per ogni studente via riga di comando
Automazione: creare 25+ account in pochi secondi con uno script
# Crea un gruppo per gli studenti della classe docker exec -u www-data nextcloud-app \ php occ group:add studenti_4A # Crea un singolo utente (ripeti per ogni studente) docker exec -u www-data nextcloud-app \ php occ user:add \ --display-name="Mario Rossi" \ --group="studenti_4A" \ --password-from-env \ mario.rossi \ <<< "Password123!" # Verifica la lista utenti docker exec -u www-data nextcloud-app php occ user:list
Per creare tutti gli utenti della classe in una volta sola, usa questo script:
#!/bin/bash
# Lista studenti: "username:NomeCompleto"
declare -a STUDENTI=(
"mario.rossi:Mario Rossi"
"giulia.bianchi:Giulia Bianchi"
"luca.ferrari:Luca Ferrari"
"anna.romano:Anna Romano"
"marco.conti:Marco Conti"
)
GRUPPO="studenti_4A"
PASSWORD_DEFAULT="Cambiam1!"
for entry in "${STUDENTI[@]}"; do
USERNAME="${entry%%:*}"
NOME="${entry#*:}"
echo "Creo utente: $USERNAME ($NOME)"
docker exec -u www-data nextcloud-app \
php occ user:add \
--display-name="$NOME" \
--group="$GRUPPO" \
--password-from-env \
"$USERNAME" <<< "$PASSWORD_DEFAULT"
done
echo "✅ Tutti gli utenti creati!"
chmod +x crea_utenti.sh bash crea_utenti.sh
- Accedere con le proprie credenziali e cambiare la password di default
- Caricare un file di testo con la propria relazione nella cartella "Compiti → Settimana_01"
- Creare una cartella condivisa "Progetto Gruppo" e condividerla con 3 compagni specificando i permessi (solo lettura vs modifica)
- Configurare il calendario condiviso della classe: ogni studente aggiunge 2 scadenze future
- Installare il client Nextcloud Desktop sul PC del laboratorio e sincronizzare i file
- Avanzato: analizza la struttura del filesystem di Nextcloud — dove vengono salvati i file? (
./data/userfiles/)
| Problema | Causa probabile | Soluzione |
|---|---|---|
| Pagina bianca o errore 502 | Container Nextcloud non ancora pronto | docker compose logs nextcloud — attendi init |
| "Trusted domain" error | IP non aggiunto ai trusted domain | Aggiungi IP in .env e ricrea il container |
| Upload fallisce su file grandi | Limite PHP o Nginx | Verifica client_max_body_size in Nginx |
| Database connection error | MariaDB non ancora pronto | Attendi healthcheck o docker compose restart nextcloud |
- Capire cosa sono i modelli LLM (Large Language Models) e come funzionano internamente
- Distinguere tra AI cloud (ChatGPT = dati su server esterni) e AI locale (tutto rimane in aula)
- Valutare criticamente le risposte dell'AI: correttezza, allucinazioni, limiti
- Comprendere il concetto di quantizzazione dei modelli (4-bit, 8-bit) per fitting in RAM
- Usare l'AI come strumento di studio per informatica, programmazione e sistemi
| Modello | Dimensione | RAM necessaria | Velocità su RPi5 | Qualità risposte | Uso consigliato |
|---|---|---|---|---|---|
llama3.2:3b | ~2 GB | 4 GB | ⚡ Veloce (~3 tok/s) | ⭐⭐⭐ | Demo, esercizi veloci |
mistral:7b | ~4 GB | 6 GB | 🐢 Media (~1.5 tok/s) | ⭐⭐⭐⭐ | Uso quotidiano classe |
phi3:mini | ~2.3 GB | 4 GB | ⚡ Veloce (~2.5 tok/s) | ⭐⭐⭐⭐ | Codice e ragionamento |
gemma2:9b | ~5.4 GB | 7 GB | 🐢 Lenta (~1 tok/s) | ⭐⭐⭐⭐⭐ | Risposte più accurate |
Installare Ollama
Il runtime che gestisce ed esegue i modelli AI localmente
Ollama è compatibile con ARM64 (architettura del Raspberry Pi 5) dalla versione 0.1.17 in poi. Installiamo l'ultima versione:
# Scarica ed esegui lo script di installazione ufficiale curl -fsSL https://ollama.com/install.sh | sh # Lo script installa Ollama come servizio systemd # Verifica che il servizio sia partito sudo systemctl status ollama # Verifica che risponda sulla porta 11434 curl http://localhost:11434 # Risposta attesa: "Ollama is running" # Controlla la versione installata ollama --version
localhost. Lo configureremo al passo 3 per renderlo accessibile da tutta la rete.Scaricare il modello AI
Il download richiede 2-4 GB e diversi minuti — pianifica in anticipo
Consiglio: scarica il modello durante la notte o prima della lezione.
# Scarica Mistral 7B (raccomandato per il laboratorio — buona qualità) ollama pull mistral # Oppure Llama3.2 3B (più veloce, consuma meno RAM) ollama pull llama3.2:3b # Vedi tutti i modelli scaricati con la loro dimensione ollama list # Test rapido: esegui il modello nel terminale # Scrivi una domanda e premi Invio — Ctrl+D per uscire ollama run mistral "Spiega il subnetting in 3 righe" # Controlla quanta RAM sta usando durante l'inferenza watch -n 1 free -h
Configurare Ollama per l'accesso dalla rete
Di default Ollama blocca connessioni esterne — va abilitato esplicitamente
# Crea un override per il servizio systemd di Ollama
sudo mkdir -p /etc/systemd/system/ollama.service.d
sudo nano /etc/systemd/system/ollama.service.d/override.conf
Inserisci questo contenuto nel file:
[Service] # Ascolta su tutte le interfacce di rete (non solo localhost) Environment="OLLAMA_HOST=0.0.0.0:11434" # Numero massimo di richieste parallele (una per studente) Environment="OLLAMA_NUM_PARALLEL=2" # Mantieni il modello in RAM per 10 minuti dopo l'uso Environment="OLLAMA_KEEP_ALIVE=10m"
# Ricarica la configurazione systemd sudo systemctl daemon-reload # Riavvia Ollama con le nuove impostazioni sudo systemctl restart ollama # Apri la porta nel firewall per la rete locale sudo ufw allow from 192.168.1.0/24 to any port 11434 # Verifica che Ollama risponda da un altro PC della rete: curl http://192.168.1.200:11434/api/tags
Installare Open WebUI — Interfaccia grafica per gli studenti
Un'interfaccia web simile a ChatGPT, accessibile da browser
# Crea la cartella dati per Open WebUI mkdir -p ~/open-webui-data # Avvia il container Open WebUI docker run -d \ --name open-webui \ --restart always \ --network host \ -v ~/open-webui-data:/app/backend/data \ -e OLLAMA_BASE_URL=http://127.0.0.1:11434 \ -e WEBUI_AUTH=true \ -e WEBUI_NAME="AI Scolastica" \ ghcr.io/open-webui/open-webui:main # Segui i log durante l'avvio (attendi messaggio "Application startup complete") docker logs -f open-webui # Apri la porta 3000 nel firewall sudo ufw allow 3000/tcp
http://192.168.1.200:3000Il primo utente che si registra diventa automaticamente amministratore. Registrati tu come docente per primo!
Configurare limiti per gli studenti (pannello admin)
Gestire l'accesso e prevenire abusi
Accedi come admin a Open WebUI → Impostazioni → Admin Panel e configura:
- Disabilita la registrazione pubblica: Enable New Sign Ups → OFF
- Crea account manualmente per ogni studente dal pannello utenti
- Imposta il modello di default per tutti gli utenti
- Abilita la cronologia conversazioni (utile per il docente per le verifiche)
# Prima ottieni il token admin (effettuando login) TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/auths/signin \ -H "Content-Type: application/json" \ -d '{"email":"admin@scuola.it","password":"AdminPass"}' | jq -r '.token') # Crea un utente studente curl -s -X POST http://localhost:3000/api/v1/auths/signup \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $TOKEN" \ -d '{ "name": "Mario Rossi", "email": "mario.rossi@studenti.it", "password": "PasswordStudente1!", "role": "user" }'
- Chiedere al modello di spiegare il subnetting con un esempio su una rete /26 — verificare matematicamente se la risposta è corretta
- Generare uno script Python che calcola tutte le sottoreti di un indirizzo dato — testarlo nel terminale
- Testare le "allucinazioni": chiedi all'AI una data storica inventata e osserva come risponde
- Confronta le risposte di Mistral vs Llama3 alla stessa domanda tecnica: quale è più preciso?
- Crea un "system prompt" che trasforma l'AI in un tutor di reti che risponde solo con esempi pratici
- Avanzato: usa l'API REST di Ollama da Python per inviare domande programmaticamente
Installare e configurare Apache2
Il web server più usato al mondo
# Installa Apache2 sudo apt install -y apache2 # Avvialo e abilita l'avvio automatico al boot sudo systemctl start apache2 sudo systemctl enable apache2 # Controlla che sia in esecuzione sudo systemctl status apache2 # Apri la porta 80 nel firewall (già aperta se hai fatto il progetto 1) sudo ufw allow 80/tcp # Test: deve rispondere con "It works!" (pagina di default Apache) curl http://localhost # Oppure apri il browser su http://192.168.1.200 # Abilita i moduli Apache necessari sudo a2enmod rewrite # URL rewriting sudo a2enmod headers # gestione header HTTP sudo a2enmod userdir # cartelle utente (~utente) sudo systemctl restart apache2
Creare utenti Linux e struttura directory per ogni studente
Ogni studente ha la sua cartella web isolata con i propri permessi
#!/bin/bash # Lista degli studenti (modifica con i nomi reali) declare -a STUDENTI=("mario.rossi" "giulia.bianchi" "luca.ferrari" "anna.romano") WEB_ROOT="/var/www/html" PASS_DEFAULT="Scuola2024!" for STUDENTE in "${STUDENTI[@]}"; do echo "▶ Configuro studente: $STUDENTE" # Crea utente Linux se non esiste già if ! id "$STUDENTE" &>/dev/null; then sudo useradd -m -s /bin/bash "$STUDENTE" echo "$STUDENTE:$PASS_DEFAULT" | sudo chpasswd echo " ✓ Utente Linux creato" fi # Crea la cartella web dello studente sudo mkdir -p "$WEB_ROOT/$STUDENTE" # Crea una pagina HTML di benvenuto sudo tee "$WEB_ROOT/$STUDENTE/index.html" > /dev/null << HTML <!DOCTYPE html> <html lang="it"> <head> <meta charset="UTF-8"> <title>Sito di $STUDENTE</title> <style>body{font-family:sans-serif;max-width:800px;margin:40px auto;padding:20px}</style> </head> <body> <h1>Benvenuto nel sito di $STUDENTE</h1> <p>Questo sito è in costruzione...</p> </body> </html> HTML # Imposta permessi: studente proprietario, Apache può leggere sudo chown -R "$STUDENTE":www-data "$WEB_ROOT/$STUDENTE" sudo chmod -R 755 "$WEB_ROOT/$STUDENTE" echo " ✓ Sito disponibile su: http://192.168.1.200/$STUDENTE" done echo "" echo "✅ Configurazione completata!"
chmod +x crea_studenti_web.sh sudo bash crea_studenti_web.sh
Configurare un Virtual Host per ogni studente (sottodominio)
Per accedere con URL tipo mario.lab.local invece di /mario.rossi
Prima configura il DNS locale nel router scolastico per risolvere *.lab.local all'IP del Raspberry. Poi crea i virtual host:
#!/bin/bash
declare -a STUDENTI=("mario.rossi" "giulia.bianchi" "luca.ferrari")
WEB_ROOT="/var/www/html"
for STUDENTE in "${STUDENTI[@]}"; do
NOME_BREVE="${STUDENTE%%.*}" # es: "mario" da "mario.rossi"
CONF_FILE="/etc/apache2/sites-available/${STUDENTE}.conf"
sudo tee "$CONF_FILE" > /dev/null << APACHE
<VirtualHost *:80>
ServerName ${NOME_BREVE}.lab.local
DocumentRoot ${WEB_ROOT}/${STUDENTE}
<Directory ${WEB_ROOT}/${STUDENTE}>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorLog \${APACHE_LOG_DIR}/${STUDENTE}_error.log
CustomLog \${APACHE_LOG_DIR}/${STUDENTE}_access.log combined
</VirtualHost>
APACHE
sudo a2ensite "${STUDENTE}.conf"
echo "✓ Virtual host creato: http://${NOME_BREVE}.lab.local"
done
sudo systemctl reload apache2
Permettere agli studenti di caricare i propri file via SCP/SFTP
Gli studenti caricano i file HTML/CSS dal loro PC direttamente sul server
Ogni studente può caricare i propri file HTML usando SCP (dalla riga di comando) o un client SFTP grafico come FileZilla:
# Comandi da eseguire SUL PC DELLO STUDENTE (non sul Raspberry) # Carica un singolo file HTML scp index.html mario.rossi@192.168.1.200:/var/www/html/mario.rossi/ # Carica una cartella intera (con CSS, immagini, etc.) scp -r ./mio-sito/* mario.rossi@192.168.1.200:/var/www/html/mario.rossi/ # Verifica il sito subito dopo: apri il browser su # http://192.168.1.200/mario.rossi
192.168.1.200, porta 22, utente mario.rossi, password quella assegnata. Poi trascina i file nella cartella di destra.Analizzare i log di Apache
Capire chi accede al sito, quando e come
# Vedi le ultime 20 richieste HTTP ricevute dal server sudo tail -20 /var/log/apache2/access.log # Monitora le richieste in tempo reale (aggiorna mentre navighi) sudo tail -f /var/log/apache2/access.log # Conta quante richieste ha ricevuto ogni sito studente sudo awk '{print $7}' /var/log/apache2/access.log | \ grep -oP '^/[^/]+' | sort | uniq -c | sort -rn | head -10 # Vedi gli IP che hanno visitato il sito sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn # Controlla gli errori (404 = pagina non trovata, 500 = errore server) sudo tail -20 /var/log/apache2/error.log
- Creare un sito web personale con: pagina home, pagina "Chi sono", pagina "Progetti" — tutto collegato con nav bar
- Aggiungere CSS esterno (file separato), immagini e font Google Fonts
- Analizzare il proprio log di accesso: quante visite ha ricevuto il tuo sito? Da quali IP?
- Creare una pagina HTML con un form (anche solo grafico, senza backend) per esercitarsi con i tag
- Avanzato: aggiungi PHP al sito — crea una pagina che mostra l'ora del server con
<?php echo date('H:i:s'); ?> - Avanzato: configura HTTPS con certificato auto-firmato per il tuo virtual host
- Progettare e implementare topologie di rete con router e switch virtuali
- Configurare il subnetting: calcolare subnet mask, indirizzi di rete, broadcast, host range
- Configurare il routing statico e capire perché non scala (poi confrontare con OSPF)
- Analizzare il traffico di rete con Wireshark: ARP, ICMP, TCP, DNS a livello di byte
- Creare e isolare VLAN su uno switch Layer 2
Installare GNS3 Server sul Raspberry Pi
Il server GNS3 gira sul Raspberry, i client si connettono dai PC degli studenti
# Installa le dipendenze per GNS3 sudo apt install -y \ python3-pip python3-venv \ qemu-kvm \ libvirt-daemon-system \ bridge-utils \ ubridge \ wireshark \ tshark # Installa GNS3 server via pip in un virtualenv isolato python3 -m venv ~/gns3-venv source ~/gns3-venv/bin/activate pip install gns3-server gns3-webclient # Aggiungi l'utente al gruppo necessario per cattura pacchetti sudo usermod -aG wireshark $USER sudo usermod -aG kvm $USER sudo usermod -aG ubridge $USER # Applica i gruppi (richiede logout/login o riavvio) sudo reboot
Creare il servizio systemd per GNS3
GNS3 deve avviarsi automaticamente e rimanere sempre disponibile
sudo nano /etc/systemd/system/gns3.service
[Unit] Description=GNS3 Network Emulator Server After=network.target [Service] Type=simple User=pi WorkingDirectory=/home/pi ExecStart=/home/pi/gns3-venv/bin/gns3server \ --host 0.0.0.0 \ --port 3080 \ --log /var/log/gns3.log Restart=on-failure RestartSec=5s [Install] WantedBy=multi-user.target
sudo systemctl daemon-reload sudo systemctl enable gns3 sudo systemctl start gns3 sudo systemctl status gns3 # Apri la porta nel firewall sudo ufw allow 3080/tcp # Verifica accesso curl http://localhost:3080/v2/version
Installare GNS3 Client sui PC degli Studenti
Il client grafico si installa su ogni PC del laboratorio e si connette al server Raspberry
- Scarica GNS3 da
https://www.gns3.com/software/downloadper Windows - Durante l'installazione scegli "Run appliances on a remote server"
- Alla prima apertura: Edit → Preferences → Server → Remote server
- Imposta Host:
192.168.1.200, Port:3080 - Testa la connessione — deve comparire il nome del server
http://192.168.1.200:3080 — nessuna installazione necessaria sui client.Creare la prima topologia: 2 PC + 1 Router
La topologia base per capire il routing IP
Topologia: PC1 (192.168.1.10/24) ──── eth0 ──── R1 ──── eth1 ──── PC2 (10.0.0.10/24) (192.168.1.1/24) (10.0.0.1/24) Configurazione R1 (router VyOS o Cisco IOL): # Configura interfaccia verso PC1 configure set interfaces ethernet eth0 address 192.168.1.1/24 set interfaces ethernet eth1 address 10.0.0.1/24 commit save exit Su PC1 — imposta IP e gateway: ip addr add 192.168.1.10/24 dev eth0 ip route add default via 192.168.1.1 Su PC2 — imposta IP e gateway: ip addr add 10.0.0.10/24 dev eth0 ip route add default via 10.0.0.1 Test connettività: PC1> ping 10.0.0.10 # deve rispondere PC1> ping 10.0.0.1 # gateway del router
Analizzare il traffico con Wireshark
Catturare e decodificare i pacchetti di rete a livello di byte
# Cattura tutti i pacchetti ICMP (ping) sull'interfaccia eth0 sudo tshark -i eth0 -f "icmp" # Cattura e salva su file per analisi in Wireshark GUI sudo tshark -i eth0 -w /tmp/cattura.pcap # Analizza un file .pcap già catturato tshark -r /tmp/cattura.pcap # Filtra solo i pacchetti ARP (risoluzione IP→MAC) sudo tshark -i eth0 -f "arp" -V | head -50 # Filtra per IP sorgente specifico sudo tshark -i eth0 -Y "ip.src == 192.168.1.10"
In GNS3, clicca con il tasto destro su un cavo e scegli "Start Capture" per aprire Wireshark automaticamente su quel link.
- Base: Configura una topologia 2-router, imposta routing statico su entrambi, verifica il ping end-to-end
- Base: Cattura un ARP request/reply con Wireshark e identifica MAC address sorgente/destinazione
- Medio: Topologia 3-router in linea (A-B-C): configura routing statico su tutti e 3 e verifica che A raggiunga C
- Medio: Crea 2 VLAN (VLAN 10 e VLAN 20) su uno switch managed e verifica che i dispositivi su VLAN diverse non si "vedano"
- Avanzato: Configura OSPF tra 3 router e analizza i pacchetti "Hello" che i router si scambiano per trovare i vicini
- Sfida: simulare un traceroute e spiegare ogni hop — perché il TTL decrementato di 1 rivela i router intermedi?
- Capire come funziona l'object detection: bounding box, confidence score, classi
- Comprendere l'edge AI: l'inferenza avviene localmente sul dispositivo, senza cloud
- Analizzare le implicazioni etiche e legali della videosorveglianza AI (GDPR)
- Capire come ottimizzare modelli per hardware limitato (quantizzazione TFLite)
Collegare e testare la webcam USB
Verifica che il sistema riconosca la webcam prima di installare Frigate
# Installa tool per gestione V4L (Video4Linux) sudo apt install -y v4l-utils ffmpeg # Lista tutti i dispositivi video riconosciuti dal sistema ls /dev/video* # Dovresti vedere /dev/video0 (e magari /dev/video1) # Ottieni informazioni dettagliate sulla webcam v4l2-ctl --device /dev/video0 --all # Elenca le risoluzioni supportate dalla webcam v4l2-ctl --device /dev/video0 --list-formats-ext # Cattura 5 secondi di video per test (crea un file test.mp4) ffmpeg -f v4l2 -i /dev/video0 -t 5 -vf scale=640:480 /tmp/test.mp4 # Scarica il file sul tuo PC per visualizzarlo # scp pi@192.168.1.200:/tmp/test.mp4 .
Creare la configurazione di Frigate
Il file config.yml definisce le telecamere e i parametri di rilevamento
# Crea le directory necessarie per Frigate mkdir -p ~/frigate/config mkdir -p ~/frigate/storage # Crea il file di configurazione principale nano ~/frigate/config/config.yml
# Disabilita MQTT (per semplicità nel laboratorio) mqtt: enabled: False # Configurazione del detector AI (usa CPU con TFLite) detectors: cpu_detector: type: cpu num_threads: 3 # usa 3 dei 4 core del RPi5 # Definizione della telecamera cameras: webcam_aula: ffmpeg: inputs: - path: /dev/video0 input_args: preset-usb-webcam # preset ottimizzato per USB roles: - detect # questa stream viene analizzata dall'AI - record # questa stream viene registrata # Parametri di rilevamento detect: enabled: True width: 640 # risoluzione per l'AI (bassa = più veloce) height: 480 fps: 5 # frame al secondo analizzati (5 = equilibrio) # Oggetti da rilevare e soglia minima di confidenza objects: track: - person # rileva persone - cat # rileva gatti (per divertimento) filters: person: min_score: 0.5 # ignora rilevamenti < 50% confidenza threshold: 0.7 # soglia per contare come "rilevato" min_area: 5000 # ignora oggetti molto piccoli # Registrazione clip quando viene rilevata una persona record: enabled: True retain: days: 1 # mantieni le registrazioni per 1 giorno events: retain: default: 10 # mantieni 10 secondi prima/dopo ogni evento # Salva snapshot JPG per ogni rilevamento snapshots: enabled: True retain: default: 7 # mantieni snapshot per 7 giorni # Directory dove salvare le registrazioni record: storage: /media/frigate
Avviare Frigate con Docker
Il container ha accesso privilegiato alla webcam e alla GPU (se disponibile)
# Crea directory per la storage dei video mkdir -p ~/frigate/storage docker run -d \ --name frigate \ --restart unless-stopped \ --privileged \ --shm-size=256m \ --mount type=bind,source=~/frigate/config,target=/config \ --mount type=bind,source=~/frigate/storage,target=/media/frigate \ --device /dev/video0 \ -e TZ=Europe/Rome \ -p 5000:5000 \ -p 8554:8554 \ ghcr.io/blakeblackshear/frigate:stable-standard-arm64 # Segui i log durante l'avvio (importante: verifica che la webcam venga trovata) docker logs -f frigate # Apri la porta nel firewall sudo ufw allow 5000/tcp
http://192.168.1.200:5000 — Vedrai la telecamera in diretta con i bounding box dei rilevamenti in tempo reale.Monitorare le prestazioni del sistema durante l'inferenza
Capire il costo computazionale dell'AI in tempo reale
# Monitora CPU, RAM e temperatura in tempo reale watch -n 1 'echo "=== CPU ===" && top -bn1 | head -5 && \ echo "=== RAM ===" && free -h && \ echo "=== TEMP ===" && vcgencmd measure_temp' # Temperatura del processore (non superare 80°C) vcgencmd measure_temp # Statistiche Frigate via API curl -s http://localhost:5000/api/stats | python3 -m json.tool
- Analizza la dashboard di Frigate: conta i rilevamenti per ora — ci sono pattern? (es. più rilevamenti durante la ricreazione?)
- Modifica
min_scoreda 0.5 a 0.9 — quanti falsi positivi spariscono? Quanti rilevamenti reali si perdono? - Misura la latenza dell'AI: quanto tempo passa tra il tuo movimento e l'apparizione del bounding box?
- Dibattito etico: questo sistema in una vera scuola viola la privacy degli studenti? Quali regole GDPR si applicano? Chi deve dare il consenso?
- Avanzato: scrivi un script Python che interroga l'API di Frigate e invia una notifica email quando viene rilevata una persona fuori orario
- Comprendere il modello publish/subscribe di MQTT e perché è ideale per IoT
- Leggere e interpretare dati da sensori fisici (temperatura, umidità, movimento)
- Creare automazioni basate su condizioni: "SE temperatura > 25°C ALLORA invia notifica"
- Progettare una dashboard professionale per la visualizzazione dati in tempo reale
- Capire i protocolli IoT: MQTT, Zigbee, Z-Wave, WiFi — differenze e casi d'uso
Installare Home Assistant Container
La piattaforma open-source per la domotica più usata al mondo
# Crea directory configurazione mkdir -p ~/homeassistant/config # Avvia Home Assistant Container docker run -d \ --name homeassistant \ --restart unless-stopped \ --privileged \ --network host \ -e TZ=Europe/Rome \ -v ~/homeassistant/config:/config \ ghcr.io/home-assistant/home-assistant:stable # Segui i log — il primo avvio richiede 2-3 minuti docker logs -f homeassistant # Apri la porta nel firewall sudo ufw allow 8123/tcp
http://192.168.1.200:8123 — Segui la procedura guidata per creare l'account amministratore e configurare la posizione geografica.Installare e configurare Mosquitto MQTT Broker
Il "postino" che riceve e distribuisce i messaggi tra sensori e Home Assistant
MQTT usa il modello publish/subscribe: i sensori pubblicano dati su "topic", Home Assistant si iscrive ai topic per riceverli.
# Installa Mosquitto broker sudo apt install -y mosquitto mosquitto-clients # Crea file di configurazione sudo tee /etc/mosquitto/conf.d/lab.conf << EOF # Ascolta su tutte le interfacce listener 1883 # Richiedi autenticazione allow_anonymous false password_file /etc/mosquitto/passwd EOF # Crea utente MQTT per Home Assistant sudo mosquitto_passwd -c /etc/mosquitto/passwd homeassistant # Inserisci la password quando richiesto: MqttHA2024! # Crea utente MQTT per i sensori/studenti sudo mosquitto_passwd /etc/mosquitto/passwd sensori # Password: MqttSensori2024! # Riavvia Mosquitto sudo systemctl restart mosquitto sudo systemctl enable mosquitto sudo systemctl status mosquitto # Apri porta nel firewall sudo ufw allow 1883/tcp # Test: pubblica un messaggio dal terminale mosquitto_pub -h localhost -u sensori -P MqttSensori2024! \ -t "aula/temperatura" -m "22.5" # Test: ascolta tutti i messaggi (apri un secondo terminale) mosquitto_sub -h localhost -u homeassistant -P MqttHA2024! \ -t "#" -v
Collegare MQTT a Home Assistant
Configurare l'integrazione MQTT in Home Assistant per ricevere i dati dei sensori
- Apri Home Assistant → Impostazioni → Dispositivi e Servizi → Aggiungi Integrazione
- Cerca e seleziona MQTT
- Inserisci: Broker =
localhost, Porta =1883, Username =homeassistant, Password =MqttHA2024! - Clicca "Invia" — deve apparire la conferma di connessione riuscita
Ora aggiungi i sensori nel file di configurazione di Home Assistant:
# Apri il file di configurazione principale
nano ~/homeassistant/config/configuration.yaml
mqtt:
sensor:
- name: "Temperatura Aula"
state_topic: "aula/temperatura"
unit_of_measurement: "°C"
device_class: temperature
value_template: "{{ value | float }}"
expire_after: 120 # considera scaduto dopo 2 minuti senza aggiornamenti
- name: "Umidità Aula"
state_topic: "aula/umidita"
unit_of_measurement: "%"
device_class: humidity
value_template: "{{ value | float }}"
- name: "CO2 Aula"
state_topic: "aula/co2"
unit_of_measurement: "ppm"
device_class: carbon_dioxide
binary_sensor:
- name: "Movimento Corridoio"
state_topic: "scuola/movimento/corridoio"
payload_on: "ON"
payload_off: "OFF"
device_class: motion
# Controlla la sintassi prima di applicare docker exec homeassistant python3 -m homeassistant --script check_config -c /config # Riavvia Home Assistant per applicare le modifiche docker restart homeassistant
Creare un sensore simulato con Python
Script che simula un sensore IoT reale pubblicando dati ogni 10 secondi
pip3 install paho-mqtt
#!/usr/bin/env python3 """ Sensore IoT simulato per il laboratorio didattico. Pubblica temperatura, umidità e CO2 via MQTT ogni 10 secondi. """ import paho.mqtt.client as mqtt import random import time import json from datetime import datetime # === CONFIGURAZIONE === BROKER_HOST = "localhost" # IP del Raspberry BROKER_PORT = 1883 USERNAME = "sensori" PASSWORD = "MqttSensori2024!" # Valori base del sensore (simulano un ambiente reale) temp_base = 21.0 # temperatura base in gradi Celsius umid_base = 55.0 # umidità base in percentuale co2_base = 800 # CO2 base in ppm (parti per milione) def on_connect(client, userdata, flags, rc): if rc == 0: print(f"✅ Connesso al broker MQTT su {BROKER_HOST}:{BROKER_PORT}") else: print(f"❌ Connessione fallita con codice: {rc}") def on_publish(client, userdata, mid): pass # callback chiamata dopo ogni publish riuscito # Crea il client MQTT client = mqtt.Client(client_id="sensore_aula_01") client.username_pw_set(USERNAME, PASSWORD) client.on_connect = on_connect client.on_publish = on_publish # Connettiti al broker client.connect(BROKER_HOST, BROKER_PORT, keepalive=60) client.loop_start() # thread in background per mantenere la connessione print("🌡 Sensore aula avviato. Premi Ctrl+C per fermare.") print("-" * 50) try: while True: # Genera valori con piccole variazioni casuali (simulano un sensore reale) temperatura = round(temp_base + random.uniform(-2.0, 2.0), 1) umidita = round(umid_base + random.uniform(-5.0, 5.0), 1) co2 = int(co2_base + random.uniform(-100, 200)) # Simula aumento CO2 durante l'ora di lezione ora = datetime.now().hour if 8 <= ora <= 13: co2 += 200 # più persone in classe = più CO2 timestamp = datetime.now().strftime("%H:%M:%S") print(f"[{timestamp}] 🌡 {temperatura}°C | 💧 {umidita}% | 🌬 {co2} ppm") # Pubblica ogni valore su un topic separato client.publish("aula/temperatura", str(temperatura), qos=1) client.publish("aula/umidita", str(umidita), qos=1) client.publish("aula/co2", str(co2), qos=1) # Pubblica anche un JSON con tutti i dati insieme payload = json.dumps({ "temperatura": temperatura, "umidita": umidita, "co2": co2, "timestamp": datetime.now().isoformat() }) client.publish("aula/dati_completi", payload, qos=1) time.sleep(10) # aspetta 10 secondi prima del prossimo invio except KeyboardInterrupt: print("\n⏹ Sensore fermato.") client.loop_stop() client.disconnect()
# Esegui in background con nohup (rimane attivo dopo logout SSH) nohup python3 sensore_aula.py > /tmp/sensore.log 2>&1 & echo "PID del processo: $!" # Vedi i log del sensore tail -f /tmp/sensore.log # Per fermarlo: trova il PID e killalo ps aux | grep sensore_aula.py kill <PID>
Creare automazioni e dashboard in Home Assistant
Automazioni: "SE condizione ALLORA azione". Dashboard: visualizzazione dati live
Creare un'automazione via interfaccia grafica:
- Home Assistant → Impostazioni → Automazioni → Crea Automazione → Inizia da zero
- Trigger: Stato di un'entità → seleziona "Temperatura Aula" → supera 25°C
- Azione: Chiama un servizio → notify.persistent_notification → messaggio "🌡 Temperatura alta in aula!"
alias: "Avviso Temperatura Alta"
description: "Notifica quando la temperatura supera 25°C"
trigger:
- platform: numeric_state
entity_id: sensor.temperatura_aula
above: 25
for:
minutes: 2 # deve restare alta per 2 minuti (evita falsi allarmi)
condition: []
action:
- service: notify.persistent_notification
data:
title: "⚠️ Temperatura elevata"
message: >
La temperatura in aula è {{ states('sensor.temperatura_aula') }}°C.
Considera di aprire le finestre.
- service: logbook.log
data:
name: "Temperatura Aula"
message: "Superata soglia 25°C"
mode: single
Dashboard: Home Assistant → Overview → Modifica Dashboard → Aggiungi Scheda → scegli "Grafico Storico" o "Indicatore" per ogni sensore.
- Modifica lo script Python per aggiungere un sensore di luminosità simulato (lux) — aggiungi il topic e la card in Home Assistant
- Crea un'automazione "CO2 Alert": se CO2 > 1200ppm manda una notifica (livello non salubre per l'apprendimento)
- Progetta la dashboard della "smart classroom" con almeno 4 sensori diversi usando grafico storico, indicatori e card personalizzate
- Registra i dati per 30 minuti e analizza: la CO2 aumenta durante la lezione? Cosa suggerisce?
- Avanzato: installa Node-RED tramite Docker e crea un flusso visuale che legge da MQTT e scrive su un foglio Google Sheets
- Comprendere le categorie OWASP Top 10: le 10 vulnerabilità web più critiche
- Eseguire una scansione di rete con Nmap e interpretare correttamente i risultati
- Testare vulnerabilità SQL Injection, XSS e CSRF in ambiente sicuro (DVWA)
- Configurare tecniche di hardening: firewall, fail2ban, SSH sicuro, aggiornamenti automatici
- Analizzare i log di sistema per identificare tentativi di intrusione
Creare la rete isolata con Docker
Tutti gli attacchi avvengono in una rete virtuale senza accesso a internet
# Crea una rete Docker completamente isolata (--internal = nessun accesso esterno) docker network create \ --driver bridge \ --internal \ --subnet 10.10.10.0/24 \ --gateway 10.10.10.1 \ lab-cybersec # Verifica che la rete sia stata creata docker network inspect lab-cybersec | python3 -m json.tool # Lista tutte le reti Docker docker network ls
Installare i target vulnerabili
DVWA e Metasploitable 2: applicazioni intenzionalmente vulnerabili per l'apprendimento
# DVWA = Damn Vulnerable Web Application # Contiene SQL Injection, XSS, CSRF, File Upload, ecc. docker run -d \ --name dvwa \ --network lab-cybersec \ --ip 10.10.10.10 \ -p 8888:80 \ ghcr.io/digininja/dvwa:latest # Aspetta che il container si avvii (circa 30 secondi) sleep 30 # Verifica che sia in esecuzione docker logs dvwa | tail -5
http://192.168.1.200:8888 — Login: admin / passwordPrima di iniziare: Setup/Reset DB → Create/Reset Database. Poi: DVWA Security → imposta livello "Low" per i primi esercizi.
Installare e usare Nmap per la scansione di rete
Il tool di network scanning più usato dai professionisti della sicurezza
# Installa Nmap sudo apt install -y nmap ===== LIVELLO BASE ===== # 1. Scansione host discovery: quali dispositivi sono attivi in rete? # (Invia solo ping, non scansiona porte) sudo nmap -sn 192.168.1.0/24 # 2. Scansione rapida delle 100 porte più comuni su un host sudo nmap -F 192.168.1.200 ===== LIVELLO INTERMEDIO ===== # 3. Scansione completa con rilevamento versioni servizi # -sV = versione servizi, -O = sistema operativo sudo nmap -sV -O 192.168.1.200 # 4. Scansione SYN (più veloce e stealth) # Invia solo SYN, non completa il 3-way handshake sudo nmap -sS 192.168.1.200 # 5. Scansione di tutte e 65535 le porte sudo nmap -p- 192.168.1.200 ===== LIVELLO AVANZATO ===== # 6. Scansione aggressiva con script NSE (Nmap Scripting Engine) # -A = OS + versioni + traceroute + script sudo nmap -A 192.168.1.200 # 7. Usa script NSE per rilevare vulnerabilità HTTP sudo nmap --script=http-vuln* 192.168.1.200 # 8. Salva l'output in tutti i formati per report sudo nmap -sV -O 192.168.1.200 -oA /tmp/scansione_lab
Esercizi SQL Injection su DVWA
Capire come funziona l'attacco SQL Injection più comune
Vai in DVWA → SQL Injection → Security Level: Low. Il campo "User ID" è vulnerabile.
LIVELLO 1 — Verifica se il campo è vulnerabile: Input: 1' # Se compare un errore SQL, il campo è vulnerabile! # Errore tipo: "You have an error in your SQL syntax..." LIVELLO 2 — Bypass dell'autenticazione: Input: 1' OR '1'='1 # Restituisce TUTTI gli utenti invece di uno solo # La query diventa: WHERE id='1' OR '1'='1' (sempre vera!) LIVELLO 3 — Scopri il numero di colonne con ORDER BY: Input: 1' ORDER BY 1-- Input: 1' ORDER BY 2-- Input: 1' ORDER BY 3-- # se errore, ci sono solo 2 colonne LIVELLO 4 — UNION per estrarre dati dal DB: Input: 1' UNION SELECT user(), database()-- - # Restituisce: utente MySQL e nome del database! LIVELLO 5 — Lista tutte le tabelle del database: Input: 1' UNION SELECT table_name, NULL FROM information_schema.tables WHERE table_schema=database()-- - LIVELLO 6 — Estrai utenti e password hash: Input: 1' UNION SELECT user, password FROM users-- - # Le password sono in MD5 — già questo è grave!
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?")Esercizi XSS (Cross-Site Scripting) su DVWA
Capire come iniettare codice JavaScript malevolo in pagine web
Vai in DVWA → XSS (Reflected) → Security Level: Low.
LIVELLO 1 — XSS base: inietta un alert popup Input nel campo "What's your name": <script>alert('XSS vulnerabile!')</script> LIVELLO 2 — Ruba il cookie di sessione (simulazione) <script>alert('Cookie: ' + document.cookie)</script> # In un attacco reale, il cookie verrebbe inviato a un server esterno LIVELLO 3 — Modifica il contenuto della pagina (DOM) <script>document.body.innerHTML = '<h1>Sito hackerato!</h1>'</script> LIVELLO 4 — Redirect a sito esterno (phishing) <script>window.location='https://www.google.it'</script>
htmlspecialchars($input, ENT_QUOTES) converte <script> in <script> che il browser non esegue.Hardening del sistema — Tecniche di difesa reali
Configurare il Raspberry come server sicuro per la produzione
# Fai un backup prima di modificare sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak # Modifica la configurazione SSH sudo nano /etc/ssh/sshd_config # Modifica/aggiungi queste righe: Port 2222 # cambia la porta default (meno attacchi automatici) PermitRootLogin no # disabilita login diretto come root MaxAuthTries 3 # massimo 3 tentativi di password PasswordAuthentication yes # keep yes per il lab (in prod: no + chiavi SSH) LoginGraceTime 30 # 30 secondi per autenticarsi X11Forwarding no # disabilita X11 forwarding (non necessario) AllowUsers pi # solo l'utente pi può fare SSH # Riavvia SSH dopo le modifiche sudo systemctl restart sshd # Aggiorna la regola UFW per la nuova porta SSH sudo ufw allow 2222/tcp sudo ufw delete allow 22/tcp
# Installa Fail2ban (blocca IP dopo tentativi falliti) sudo apt install -y fail2ban # Crea configurazione personalizzata (non modificare il file .conf originale) sudo tee /etc/fail2ban/jail.local << EOF [DEFAULT] bantime = 3600 # blocca l'IP per 1 ora findtime = 600 # finestra di 10 minuti per contare i fallimenti maxretry = 5 # blocca dopo 5 tentativi falliti [sshd] enabled = true port = 2222 # porta SSH che hai configurato logpath = %(sshd_log)s maxretry = 3 # per SSH ancora più restrittivo [apache-auth] enabled = true [nginx-http-auth] enabled = true EOF # Avvia e abilita Fail2ban sudo systemctl enable fail2ban sudo systemctl restart fail2ban # Controlla lo stato delle "prigioni" attive sudo fail2ban-client status # Controlla lo stato della jail SSH sudo fail2ban-client status sshd # Sblocca un IP bloccato per errore # sudo fail2ban-client set sshd unbanip 192.168.1.X
# Vedi tutti i tentativi di login SSH falliti sudo grep "Failed password" /var/log/auth.log | tail -20 # Conta i tentativi falliti per IP (chi attacca di più?) sudo grep "Failed password" /var/log/auth.log | \ awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10 # Vedi i login riusciti sudo grep "Accepted" /var/log/auth.log | tail -10 # Monitor in tempo reale dei log di autenticazione sudo tail -f /var/log/auth.log | grep --line-buffered "Failed\|Accepted\|Invalid"
- Scansiona la rete del laboratorio con Nmap e crea un report professionale: quanti host attivi, quali porte aperte, quali servizi identificati
- Completa tutti e 6 i livelli di SQL Injection su DVWA (Low → Medium → High) e documenta le differenze nelle tecniche difensive
- Esegui gli esercizi XSS Reflected e XSS Stored su DVWA — qual è la differenza? Quale è più pericoloso e perché?
- Configura Fail2ban e simulalo: esegui 6 tentativi SSH falliti di fila — il tuo IP viene bloccato?
- Analizza i log auth.log: quanti tentativi falliti ci sono? Da quanti IP diversi? Probabilmente ci sono già scanner automatici!
- Avanzato: installa Metasploit Framework e usa il modulo
scanner/http/http_versionper rilevare le versioni dei web server in rete - Presentazione finale: ogni studente sceglie una vulnerabilità OWASP Top 10, la dimostra su DVWA e spiega come si previene in produzione
🌐 Fondamenti Server e Linux
- Setup completo Raspberry Pi OS
- Comandi Linux essenziali e gestione file
- Utenti, gruppi e permessi su Linux
- Web Server Apache — Progetto 3
- Cloud Nextcloud — Progetto 1
- Introduzione a Docker e container
- Configurazione Firewall UFW
🔀 Reti, Protocolli e AI
- Subnetting e indirizzamento IP
- Simulazione reti con GNS3 — Progetto 4
- Analisi traffico con Wireshark
- Routing statico e OSPF
- Server AI Locale Ollama — Progetto 2
- Modelli LLM e prompt engineering
- Containerizzazione avanzata con Docker Compose
🚀 Progetti Avanzati a Scelta
- Smart Home IoT — Progetto 6
- oppure: Cybersecurity Lab — Progetto 7
- oppure: Videosorveglianza AI — Progetto 5
- Progetto finale integrativo di gruppo
- Documentazione tecnica del progetto
- Presentazione finale alla classe
- Raspberry Pi 5 acceso e raggiungibile via SSH all'IP statico configurato
- Docker in esecuzione:
docker psmostra i container attivi del giorno - Firewall attivo con le porte corrette aperte:
sudo ufw status - Spazio su disco sufficiente:
df -h /(almeno 50GB liberi) - RAM disponibile:
free -h(almeno 2GB liberi senza carico) - Temperatura del processore:
vcgencmd measure_temp(sotto 60°C a riposo) - Backup della configurazione Docker Compose e file .env in luogo sicuro
# Aggiorna il sistema operativo sudo apt update && sudo apt upgrade -y && sudo apt autoremove -y # Aggiorna tutte le immagini Docker all'ultima versione docker images | awk 'NR>1 {print $1":"$2}' | xargs -I {} docker pull {} # Riavvia tutti i container con le nuove immagini cd ~/nextcloud && docker compose pull && docker compose up -d # Controlla lo spazio su disco usato da Docker docker system df # Rimuovi immagini e container inutilizzati (libera spazio) docker system prune -f # Controlla la temperatura vcgencmd measure_temp # Controlla Fail2ban sudo fail2ban-client status sshd