VPS Linux come nodo centrale: ricezione, automazione e pubblicazione dei dati — Parte 3

Immagine generata.

Abstract

Una Virtual Private Server può svolgere contemporaneamente il ruolo di server web, punto di ricezione per nodi remoti, archivio storico, sistema di elaborazione e piattaforma di automazione. In una infrastruttura di misura distribuita, la VPS non deve limitarsi ad accettare richieste HTTP: deve separare i servizi, validare i dati, proteggere i file, controllare i processi, registrare gli errori e mantenere una struttura ripristinabile.

La configurazione descritta utilizza un sistema Linux con Apache, PHP, HTTPS, firewall, Fail2ban, servizi systemd, timer e attività cron. I nodi inviano misure tramite richieste POST cifrate. Gli endpoint PHP verificano presenza, tipo e intervallo dei parametri prima della scrittura. I dati vengono conservati in file CSV, trasformati in JSON e pubblicati mediante pagine HTML e JavaScript.

L’articolo considera l’intera VPS come sistema: organizzazione del filesystem, VirtualHost, TLS, permessi, concorrenza sulle scritture, log, processi automatici, gestione degli errori, backup, verifica operativa e criteri per passare da un archivio CSV a un database. Tutti i nomi di dominio, gli indirizzi e le credenziali presenti negli esempi sono segnaposto generici.

Ruolo della VPS nell’architettura

La VPS costituisce il livello intermedio tra i dispositivi fisici e l’interfaccia consultata dall’utente. La catena funzionale può essere rappresentata così:

sensori
   |
microcontrollori
   |
HTTPS POST
   |
Apache
   |
endpoint PHP
   |
validazione
   |
archivio CSV
   |
API JSON
   |
pagina HTML e grafici

Ogni livello ha una responsabilità distinta. Il microcontrollore acquisisce e filtra. Apache termina la connessione HTTP o HTTPS. PHP valida e registra. Il filesystem conserva. Un endpoint di lettura prepara i dati. Il browser rappresenta il risultato.

Questa separazione impedisce che la pagina web debba conoscere la struttura interna del firmware e impedisce al nodo remoto di modificare direttamente l’archivio.

Configurazione di base del sistema

Gli esempi assumono una distribuzione Linux con gestione dei servizi tramite systemd. Le informazioni principali possono essere controllate con:

hostnamectl
uname -r
cat /etc/os-release
ip address show
ip route show

La configurazione di rete pubblica deve essere verificata senza pubblicare indirizzi reali nei file distribuiti. Negli esempi viene usato l’indirizzo riservato alla documentazione 203.0.113.10 e il dominio example.net.

L’accesso amministrativo avviene tramite SSH. Una configurazione client con keepalive riduce la probabilità che una sessione venga interrotta da apparati intermedi:

ssh \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=120 \
  -o TCPKeepAlive=yes \
  admin@203.0.113.10

Le credenziali, le chiavi private e gli indirizzi effettivi non devono comparire nell’articolo, nei repository o negli script pubblicati.

Organizzazione del filesystem

Una struttura ordinata separa i contenuti pubblici, gli script, gli archivi e i file di servizio:

/var/www/example.net/
├── index.html
├── stile.css
├── consensi.js
├── strumenti/
│   ├── index.html
│   ├── ricezione/
│   │   ├── logger.php
│   │   ├── get_data.php
│   │   ├── dati.csv
│   │   └── index.html
│   └── immagini/
└── privacy.html

/opt/telemetria/
├── bin/
│   ├── genera_previsioni.sh
│   ├── crea_snapshot.py
│   └── verifica_rete.sh
├── config/
└── state/

/var/log/telemetria/
├── acquisizione.log
├── previsioni.log
└── errori.log

I file direttamente scaricabili dal browser devono rimanere nel DocumentRoot. Gli script amministrativi, le configurazioni e gli stati interni dovrebbero essere collocati fuori dall’albero web.

Se un CSV non deve essere scaricato direttamente, può essere spostato fuori dal DocumentRoot e letto da PHP mediante un percorso assoluto autorizzato.

Apache e VirtualHost

Un VirtualHost separa dominio, DocumentRoot e log applicativi. Una configurazione HTTP minima può essere:

<VirtualHost *:80>
    ServerName example.net
    ServerAlias www.example.net

    DocumentRoot /var/www/example.net

    <Directory /var/www/example.net>
        Options -Indexes +FollowSymLinks
        AllowOverride None
        Require all granted
    </Directory>

    ErrorLog ${APACHE_LOG_DIR}/example-error.log
    CustomLog ${APACHE_LOG_DIR}/example-access.log combined
</VirtualHost>

Options -Indexes impedisce la generazione automatica dell’elenco dei file quando manca un documento indice. AllowOverride None evita che file .htaccess modifichino la configurazione senza un controllo esplicito.

Dopo la modifica è necessario verificare la sintassi prima del reload:

sudo apachectl configtest
sudo systemctl reload apache2
sudo systemctl status apache2

HTTPS e terminazione TLS

La comunicazione dai nodi deve utilizzare HTTPS. Il certificato associa il dominio alla chiave pubblica del server e permette al client di verificare l’identità del destinatario.

Un VirtualHost TLS contiene almeno:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName example.net
    DocumentRoot /var/www/example.net

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.net/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.net/privkey.pem

    ErrorLog ${APACHE_LOG_DIR}/example-ssl-error.log
    CustomLog ${APACHE_LOG_DIR}/example-ssl-access.log combined
</VirtualHost>
</IfModule>

Il file della chiave privata deve essere leggibile soltanto dai processi autorizzati. Non deve essere copiato nel DocumentRoot, inviato ai nodi o incluso in backup pubblici.

La validità del certificato e la configurazione del server possono essere controllate con:

sudo certbot certificates
sudo apachectl configtest
openssl s_client \
  -connect example.net:443 \
  -servername example.net

Endpoint di ricezione

L’endpoint riceve dati mediante POST. Non deve accettare una riga già formattata dal client: deve ricevere campi distinti e costruire autonomamente il record finale.

Un esempio per temperatura, umidità, pressione e tensione è:

<?php
declare(strict_types=1);

date_default_timezone_set("UTC");
header("Content-Type: text/plain; charset=UTF-8");

$logfile = __DIR__ . "/dati.csv";

function errore(int $codice, string $messaggio): void {
    http_response_code($codice);
    echo "ERRORE: $messaggio\n";
    exit;
}

$tempRaw  = $_POST["temp"]  ?? "";
$humRaw   = $_POST["hum"]   ?? "";
$pressRaw = $_POST["press"] ?? "";
$voltRaw  = $_POST["volt"]  ?? "";

if (
    $tempRaw === "" ||
    $humRaw === "" ||
    $pressRaw === "" ||
    $voltRaw === ""
) {
    errore(400, "parametro mancante");
}

if (
    !is_numeric($tempRaw) ||
    !is_numeric($humRaw) ||
    !is_numeric($pressRaw) ||
    !is_numeric($voltRaw)
) {
    errore(400, "parametro non numerico");
}

$temp  = (float)$tempRaw;
$hum   = (float)$humRaw;
$press = (float)$pressRaw;
$volt  = (float)$voltRaw;

if (!is_finite($temp) || $temp < -10.0 || $temp > 50.0) {
    errore(400, "temperatura fuori scala");
}

if (!is_finite($hum) || $hum < 0.0 || $hum > 100.0) {
    errore(400, "umidita fuori scala");
}

if (!is_finite($press) || $press < 850.0 || $press > 1100.0) {
    errore(400, "pressione fuori scala");
}

if (!is_finite($volt) || $volt < 0.0 || $volt > 5.2) {
    errore(400, "tensione fuori scala");
}

$line = sprintf(
    "%s\t%.2f\t%.2f\t%.2f\t%.3f\n",
    date("Y-m-d H:i:s"),
    $temp,
    $hum,
    $press,
    $volt
);

if (file_put_contents(
    $logfile,
    $line,
    FILE_APPEND | LOCK_EX
) === false) {
    errore(500, "scrittura file");
}

echo "OK: $line";
?>

Il timestamp viene generato dal server in UTC. Il client non può quindi retrodatare, anticipare o modificare l’ora della riga.

Validazione strutturale e fisica

La validazione deve avvenire in più fasi:

  1. verifica della presenza dei parametri;
  2. verifica del formato numerico;
  3. conversione esplicita nel tipo previsto;
  4. rifiuto di valori non finiti;
  5. controllo dell’intervallo fisico ammesso;
  6. formattazione controllata della riga;
  7. scrittura protetta da blocco esclusivo.

Il controllo sul server non sostituisce quello del firmware. I due livelli proteggono da categorie di errore differenti: il nodo intercetta letture anomale prima della trasmissione; il server protegge lo storico da firmware difettoso, richieste incomplete o chiamate costruite esternamente.

Concorrenza sulla scrittura

Più nodi possono contattare lo stesso endpoint nello stesso intervallo. Senza un lock, due processi PHP potrebbero aprire il file e scrivere contemporaneamente, producendo righe interrotte o sovrapposte.

file_put_contents(
    $logfile,
    $line,
    FILE_APPEND | LOCK_EX
);

FILE_APPEND aggiunge la riga senza cancellare il contenuto esistente. LOCK_EX richiede un blocco esclusivo durante la scrittura.

Il lock riduce il rischio di collisione, ma non trasforma il CSV in un database transazionale. Se il numero di nodi o la frequenza cresce, è necessario valutare un archivio progettato per accessi concorrenti.

Permessi e proprietà dei file

Apache deve poter leggere i contenuti pubblici e scrivere soltanto nei file destinati alla registrazione. Non è necessario rendere l’intero sito scrivibile dal processo web.

sudo chown -R root:www-data /var/www/example.net
sudo find /var/www/example.net -type d -exec chmod 750 {} \;
sudo find /var/www/example.net -type f -exec chmod 640 {} \;

sudo touch /var/www/example.net/strumenti/ricezione/dati.csv
sudo chown www-data:www-data \
  /var/www/example.net/strumenti/ricezione/dati.csv
sudo chmod 640 \
  /var/www/example.net/strumenti/ricezione/dati.csv

I permessi effettivi devono essere verificati con l’utente del processo Apache:

sudo -u www-data test -r /var/www/example.net/index.html
sudo -u www-data test -w \
  /var/www/example.net/strumenti/ricezione/dati.csv

Endpoint JSON per i grafici

La pagina web non dovrebbe interpretare direttamente ogni variante del CSV. Un endpoint intermedio seleziona la variabile, filtra i dati e restituisce JSON.

<?php
declare(strict_types=1);

header("Content-Type: application/json; charset=UTF-8");

$param = $_GET["param"] ?? "temp";

$map = [
    "temp"  => 2,
    "hum"   => 3,
    "press" => 4,
    "volt"  => 5,
];

if (!array_key_exists($param, $map)) {
    http_response_code(400);
    echo json_encode(["errore" => "parametro non valido"]);
    exit;
}

$file = __DIR__ . "/dati.csv";
$righe = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

$labels = [];
$data = [];

foreach ($righe as $riga) {
    $parti = preg_split('/\s+/', trim($riga));

    if ($parti === false || count($parti) < 6) {
        continue;
    }

    $valore = str_replace(",", ".", trim($parti[$map[$param]]));

    if ($valore === "" || !is_numeric($valore)) {
        continue;
    }

    $numero = (float)$valore;

    $limiti = [
        "temp"  => [-10.0, 50.0],
        "hum"   => [0.0, 100.0],
        "press" => [850.0, 1100.0],
        "volt"  => [0.0, 5.2],
    ];

    [$min, $max] = $limiti[$param];

    if (!is_finite($numero) || $numero < $min || $numero > $max) {
        continue;
    }

    $labels[] = $parti[0] . " " . $parti[1];
    $data[] = $numero;
}

echo json_encode(
    ["labels" => $labels, "data" => $data],
    JSON_UNESCAPED_UNICODE
);
?>

L’endpoint effettua una seconda lettura difensiva. Anche se il logger è stato corretto, lo storico può contenere righe prodotte da versioni precedenti o inserite manualmente.

Separazione tra dati vecchi e nuova configurazione

Quando cambia il significato di una colonna, la serie non può essere interpretata come omogenea. Un caso tipico è la sostituzione di un valore RAW con una tensione espressa in volt.

Le opzioni corrette sono:

  • creare un nuovo file;
  • aggiungere una colonna di versione;
  • definire una data di discontinuità;
  • filtrare esplicitamente i valori incompatibili;
  • archiviare lo storico precedente separatamente.

Cambiare soltanto l’etichetta del grafico non modifica il significato dei dati già registrati.

Servizi systemd

Le elaborazioni periodiche più importanti dovrebbero essere eseguite come servizi systemd. Un’unità di tipo oneshot può essere:

[Unit]
Description=Generazione dei prodotti previsionali
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
User=telemetria
Group=telemetria
WorkingDirectory=/opt/telemetria
ExecStart=/opt/telemetria/bin/genera_previsioni.sh

NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/opt/telemetria/state /var/www/example.net/dati

[Install]
WantedBy=multi-user.target

Type=oneshot indica un processo che esegue un’attività e termina. Le direttive di hardening limitano l’accesso al sistema oltre quanto necessario.

Dopo aver creato o modificato l’unità:

sudo systemctl daemon-reload
sudo systemctl start telemetria-forecast.service
sudo systemctl status telemetria-forecast.service
sudo journalctl -u telemetria-forecast.service

Timer systemd

Un timer avvia il servizio secondo un calendario:

[Unit]
Description=Avvio periodico della generazione previsionale

[Timer]
OnCalendar=*-*-* 00,06,12,18:00:00 UTC
Persistent=true
Unit=telemetria-forecast.service

[Install]
WantedBy=timers.target

Persistent=true permette di recuperare un’esecuzione persa durante uno spegnimento, avviandola al ritorno del sistema.

sudo systemctl enable --now telemetria-forecast.timer
systemctl list-timers --all
systemctl status telemetria-forecast.timer

Per le attività critiche il timer è preferibile a cron perché integra dipendenze, stato, journal, controllo degli errori e avvio manuale del servizio associato.

Attività cron

Cron rimane adeguato per comandi brevi e indipendenti. Un esempio:

*/5 * * * * /opt/telemetria/bin/verifica_rete.sh \
  >> /var/log/telemetria/rete.log 2>&1

0,30 * * * * /opt/telemetria/bin/crea_snapshot.py \
  >> /var/log/telemetria/snapshot.log 2>&1

L’ambiente di cron è ridotto. Gli script devono usare percorsi assoluti, impostare esplicitamente le variabili necessarie e reindirizzare sia lo standard output sia lo standard error.

crontab -l
sudo crontab -l
systemctl status cron

Logging applicativo e di sistema

Un errore deve lasciare una traccia verificabile. Le principali fonti sono:

  • log di accesso Apache;
  • log degli errori Apache;
  • journal dei servizi systemd;
  • log espliciti degli script;
  • messaggi restituiti dagli endpoint;
  • timestamp e dimensione dei file prodotti.
sudo tail -f /var/log/apache2/example-access.log
sudo tail -f /var/log/apache2/example-error.log
sudo journalctl -u telemetria-forecast.service -f
sudo journalctl --since "1 hour ago" -p warning

La presenza di un file aggiornato non dimostra da sola che il contenuto sia corretto. Devono essere verificati anche numero di righe, timestamp, formato, intervalli e continuità temporale.

Firewall UFW

Una VPS web necessita normalmente delle porte SSH, HTTP e HTTPS. Le regole minime possono essere:

sudo ufw default deny incoming
sudo ufw default allow outgoing

sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

sudo ufw enable
sudo ufw status verbose

Prima di abilitare il firewall è necessario verificare che la porta SSH utilizzata sia autorizzata. Una regola errata può rendere la VPS irraggiungibile dall’esterno.

Se l’accesso amministrativo proviene da un intervallo stabile, la regola SSH può essere limitata alla rete autorizzata.

Fail2ban

Fail2ban analizza i log e applica blocchi temporanei agli indirizzi che mostrano comportamenti ripetitivi compatibili con tentativi di accesso o scansione.

Una configurazione locale evita di modificare direttamente i file distribuiti dal pacchetto:

[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 5
findtime = 10m
bantime = 1h
sudo systemctl restart fail2ban
sudo fail2ban-client status
sudo fail2ban-client status sshd

Fail2ban non sostituisce autenticazione con chiave, aggiornamenti, firewall e riduzione dei servizi esposti.

Protezione degli endpoint

La cifratura non impedisce a un soggetto esterno di inviare una richiesta valida al dominio. Per distinguere i nodi autorizzati è possibile utilizzare un token applicativo trasmesso in un header.

Authorization: Bearer TOKEN_LUNGO_E_CASUALE

Sul server il confronto deve essere eseguito senza inserire il token nel codice pubblicato:

$tokenRicevuto = $_SERVER["HTTP_AUTHORIZATION"] ?? "";
$tokenAtteso = getenv("TELEMETRIA_TOKEN") ?: "";

if (!hash_equals(
    "Bearer " . $tokenAtteso,
    $tokenRicevuto
)) {
    http_response_code(401);
    exit("ERRORE: non autorizzato\n");
}

Il segreto può essere fornito al processo tramite una variabile di ambiente o un file esterno con permessi restrittivi. Non deve apparire nell’HTML, nel JavaScript o nel repository pubblico.

Controllo della dimensione delle richieste

Un endpoint destinato a pochi valori numerici non deve accettare corpi arbitrariamente grandi. Apache e PHP possono imporre limiti.

<Location "/strumenti/ricezione/logger.php">
    LimitRequestBody 4096
</Location>

Anche il numero di richieste può essere limitato mediante moduli Apache, reverse proxy o firewall applicativi. La soglia deve essere coerente con la frequenza reale dei nodi e con il comportamento di retry previsto dal firmware.

Backup dei file operativi

Prima di modificare un file in produzione viene creata una copia con timestamp:

sudo cp -a logger.php \
  logger.php.bak-$(date +%F-%H%M%S)

sudo cp -a dati.csv \
  dati.csv.bak-$(date +%F-%H%M%S)

cp -a conserva metadati e permessi. La copia locale è utile per un rollback rapido, ma non sostituisce un backup esterno: un guasto del filesystem può eliminare originale e copia.

Per un archivio coerente è opportuno fermare temporaneamente le scritture oppure utilizzare uno snapshot del filesystem o del volume.

Rotazione e crescita degli archivi

I CSV e i log crescono continuamente. Devono essere controllati per evitare l’esaurimento dello spazio disco:

df -h
du -h /var/www/example.net --max-depth=3
wc -l /var/www/example.net/strumenti/ricezione/dati.csv
stat /var/www/example.net/strumenti/ricezione/dati.csv

Strategie possibili:

  • un file per anno;
  • un file per mese;
  • compressione degli archivi chiusi;
  • retention differente per dati grezzi e aggregati;
  • migrazione a database quando la ricerca diventa costosa.

I file ancora utilizzati dal logger non devono essere compressi o rinominati senza coordinare il processo di scrittura.

Controlli di integrità del CSV

Una verifica periodica può individuare righe con numero errato di colonne o valori fuori scala:

awk '
BEGIN { errori = 0 }
{
    if (NF != 6) {
        print "COLONNE", NR, $0
        errori++
        next
    }

    temp  = $3 + 0
    hum   = $4 + 0
    press = $5 + 0
    volt  = $6 + 0

    if (temp < -10 || temp > 50 ||
        hum < 0 || hum > 100 ||
        press < 850 || press > 1100 ||
        volt < 0 || volt > 5.2) {
        print "VALORI", NR, $0
        errori++
    }
}
END {
    print "Errori:", errori
}' dati.csv

Prima di eliminare righe anomale deve essere creata una copia integrale. La pulizia non deve distruggere la possibilità di ricostruire l’origine dell’errore.

Test degli endpoint

Un endpoint deve essere testato sia con valori validi sia con richieste errate:

curl -i -X POST \
  https://example.net/strumenti/ricezione/logger.php \
  -d "temp=21.50" \
  -d "hum=48.20" \
  -d "press=1013.10" \
  -d "volt=4.850"
curl -i -X POST \
  https://example.net/strumenti/ricezione/logger.php \
  -d "temp=999" \
  -d "hum=48.20" \
  -d "press=1013.10" \
  -d "volt=4.850"

Il primo test deve produrre una risposta 2xx e una nuova riga. Il secondo deve produrre un errore 400 senza modificare il CSV.

tail -1 dati.csv
wc -l dati.csv
php -l logger.php
php -l get_data.php

Verifica dei servizi automatici

La presenza del timer non garantisce che il servizio termini con successo. Devono essere controllati timer, ultima esecuzione, codice di uscita e file generati:

systemctl list-timers --all
systemctl status telemetria-forecast.timer
systemctl status telemetria-forecast.service
journalctl -u telemetria-forecast.service --since today
stat /var/www/example.net/dati/previsione.json

Uno script deve restituire codice zero soltanto quando tutte le operazioni necessarie sono state completate. Un comando interno fallito non deve essere mascherato dalla prosecuzione dello script.

#!/usr/bin/env bash
set -Eeuo pipefail

trap 'echo "errore alla riga $LINENO" >&2' ERR

/usr/bin/python3 /opt/telemetria/bin/elabora.py
/usr/bin/install -m 0644 \
  /opt/telemetria/state/output.json \
  /var/www/example.net/dati/previsione.json

Sincronizzazione temporale

Il server assegna i timestamp e deve mantenere l’orologio sincronizzato. Lo stato può essere controllato con:

timedatectl status
timedatectl timesync-status

Gli archivi tecnici dovrebbero usare UTC. La conversione nel fuso locale viene applicata durante la visualizzazione o la generazione dei report. In questo modo i dati non diventano ambigui durante i cambi tra ora solare e ora legale.

Disponibilità e gestione degli errori

La VPS può essere raggiungibile mentre un singolo servizio è guasto. Occorre distinguere almeno:

  • host non raggiungibile;
  • porta 443 chiusa;
  • Apache arrestato;
  • certificato scaduto o non valido;
  • errore PHP;
  • CSV non scrivibile;
  • filesystem pieno;
  • timer attivo ma servizio fallito;
  • pagina disponibile ma dati non aggiornati.

Ogni livello richiede un controllo differente. Un singolo ping non può dimostrare il corretto funzionamento dell’intera catena.

Quando il CSV non è più sufficiente

Il CSV rimane adeguato quando:

  • il numero di nodi è ridotto;
  • la frequenza di scrittura è bassa;
  • le query sono semplici;
  • la modifica dei record è rara;
  • il file può essere elaborato sequenzialmente.

Un database diventa preferibile quando servono:

  • scritture concorrenti frequenti;
  • ricerca rapida per intervallo temporale;
  • più nodi con metadati;
  • vincoli di unicità;
  • transazioni;
  • aggregazioni complesse;
  • retention automatica;
  • controlli di accesso più granulari.

La migrazione non deve essere anticipata soltanto per rendere il sistema più complesso. Deve risolvere un limite misurabile della soluzione esistente.

Sequenza completa di messa in servizio

  1. aggiornare il sistema;
  2. creare un utente amministrativo e configurare SSH;
  3. abilitare il firewall;
  4. installare Apache e PHP;
  5. creare il DocumentRoot;
  6. configurare il VirtualHost HTTP;
  7. verificare la risoluzione DNS;
  8. installare il certificato TLS;
  9. abilitare il VirtualHost HTTPS;
  10. creare l’endpoint di ricezione;
  11. impostare proprietà e permessi;
  12. testare richieste valide e non valide;
  13. creare l’endpoint JSON;
  14. pubblicare la pagina di visualizzazione;
  15. creare servizi e timer;
  16. configurare i log;
  17. abilitare Fail2ban;
  18. definire backup e retention;
  19. controllare spazio disco e sincronizzazione temporale;
  20. documentare ogni modifica hardware o software.

Controlli periodici

Una verifica periodica dovrebbe comprendere:

sudo apt update
sudo apt list --upgradable

systemctl --failed
systemctl list-timers --all

sudo apachectl configtest
sudo systemctl status apache2

sudo ufw status verbose
sudo fail2ban-client status

sudo certbot certificates

df -h
find /var/www/example.net -type f -size +100M -ls

journalctl -p err --since "7 days ago"

Gli aggiornamenti non devono essere applicati alla cieca su un sistema operativo. Prima di modificare componenti centrali è necessario disporre di backup, controllare lo spazio disponibile e prevedere una procedura di rollback.

Limiti della configurazione

La struttura descritta non elimina tutti i punti di guasto. Una singola VPS rimane un singolo nodo: un problema del provider, del filesystem, del sistema operativo o della configurazione può interrompere contemporaneamente ricezione e pubblicazione.

Inoltre:

  • il CSV non garantisce transazioni complete;
  • il lock non protegge da ogni interruzione improvvisa;
  • HTTPS non garantisce la correttezza fisica dei dati;
  • Fail2ban non sostituisce una corretta autenticazione;
  • un backup sullo stesso disco non protegge dal guasto del disco;
  • un timer attivo non dimostra che l’output sia corretto;
  • la disponibilità della pagina non dimostra l’aggiornamento dei dati.

Conclusioni

Una VPS destinata alla raccolta di misure è un sistema composto da più livelli coordinati. Apache gestisce le richieste, PHP valida i parametri, il filesystem conserva le serie, gli endpoint JSON preparano i dati e il browser li rappresenta. Systemd e cron eseguono elaborazioni periodiche, mentre firewall, Fail2ban, permessi e TLS riducono l’esposizione del sistema.

L’affidabilità dipende soprattutto dalla separazione delle responsabilità. Il nodo remoto non deve controllare il formato finale dell’archivio. Il server non deve accettare dati senza validarli. La pagina web non deve interpretare direttamente ogni variante del file grezzo. Gli script automatici devono produrre log, codici di uscita e file verificabili.

La VPS diventa così il nodo centrale dell’infrastruttura: riceve, controlla, archivia, elabora, pubblica e rende ripetibili operazioni che sui dispositivi periferici sarebbero difficili da gestire.

Fonti principali

Documentazione del sistema operativo Linux utilizzato.
Apache HTTP Server, documentazione ufficiale.
PHP, documentazione ufficiale del linguaggio e del runtime.
systemd, documentazione delle unità service e timer.
OpenSSH, documentazione del client e del server.
UFW, documentazione del firewall.
Fail2ban, documentazione del sistema di prevenzione.
Let’s Encrypt e Certbot, documentazione per i certificati TLS.
RFC 9110, HTTP Semantics.
RFC 8259, The JavaScript Object Notation Data Interchange Format.
Codice operativo degli endpoint e dei servizi descritti nell’articolo.