Sviluppatore sparito con le credenziali del server: come ho restituito a un cliente genovese il controllo di un Hetzner AX51-NVMe in sei giorni tra trasferimento di proprietà, rescue boot e audit completo

Sviluppatore sparito con le credenziali del server: come ho restituito a un cliente genovese il controllo di un Hetzner AX51-NVMe in sei giorni tra trasferimento di proprietà, rescue boot e audit completo

Cosa succede davvero quando lo sviluppatore freelance sparisce e l'account Hetzner è intestato a lui?

L'11 maggio 2025 mi ha chiamato il titolare di una PMI genovese che distribuisce componenti e accessoristica per la nautica da diporto nel Mediterraneo occidentale, con un fatturato intorno ai nove milioni di euro e un e-commerce B2B che gira su Magento 2 affiancato da un gestionale custom in PHP 8.1 per la sincronizzazione dei listini con i cantieri clienti. Il server era un Hetzner AX51-NVMe nel datacenter di Falkenstein - Ryzen 7 3700X, 64 GB di RAM, due NVMe da 1 TB in RAID 1 software - messo in piedi quattro anni prima dal freelance che aveva costruito l'intero stack applicativo e che, dalla fine di aprile, non rispondeva più né al telefono né alle PEC.

Il problema non era che il server fosse lento o compromesso. Girava perfettamente, Magento serviva i catalogatori dei cantieri, il gestionale continuava a spingere listini via cron ogni notte. Il problema era un altro, ed era molto peggio: l'account Hetzner Robot era registrato con l'email personale del freelance (un dominio gmail con il suo nome), la password di root SSH la conosceva solo lui, il certificato Let's Encrypt del dominio principale scadeva il 6 giugno 2025 - ventisei giorni dopo la mia telefonata - e il cron che gestiva il rinnovo via certbot renew nessuno sapeva se funzionasse davvero o se il freelance lo rinnovasse a mano. Il titolare aveva in mano soltanto il dominio (questo per fortuna sì, registrato presso un registrar italiano con le sue credenziali personali) e l'accesso backoffice di Magento come admin.

In sei giorni abbiamo recuperato tutto: trasferimento di proprietà dell'account Hetzner intestato alla S.r.l., reset della root via rescue boot, audit completo dei servizi installati, rinnovo dei certificati, rotazione integrale delle credenziali e documentazione runbook consegnata al titolare. In questo articolo ti racconto esattamente come, con i tempi reali, i comandi effettivi e le procedure legali - perché è uno scenario che nelle PMI italiane si ripete più spesso di quanto pensi, e la risposta nei primi giorni decide se il business sopravvive o se si ferma tre settimane aspettando che qualcuno capisca come entrare in una macchina di cui nessuno possiede la chiave.

Stai cercando un Consulente Informatico esperto per gestire un'emergenza di questo tipo sulla tua infrastruttura? Nel mio profilo professionale trovi l'esperienza concreta su server dedicati Hetzner, OVH e Aruba, hardening Linux e recupero di infrastrutture abbandonate. Contattami per una consulenza diretta.

Il giorno zero: mappare cosa è intestato a chi prima di toccare qualsiasi cosa

La tentazione, in questi casi, è partire subito con i comandi - rescue mode, reset della root, via. È un errore. Prima di qualsiasi intervento tecnico devi mappare la proprietà legale di ogni risorsa coinvolta, perché se il server è intestato a un terzo non puoi semplicemente "prenderne possesso": devi trasferirlo formalmente, altrimenti rischi un contenzioso civile o, nel peggiore dei casi, un esposto per accesso abusivo a sistema informatico ex art. 615-ter del codice penale. Non è teoria paranoica: nei quattro anni in cui ho gestito subentri di questo tipo ho incontrato due casi in cui lo sviluppatore originario è ricomparso mesi dopo rivendicando proprietà della macchina, e le uniche difese che hanno retto sono state quelle con la carta in ordine.

La prima mattina l'ho passata al telefono con il titolare e col suo commercialista a compilare l'elenco completo delle risorse infrastrutturali e verificare chi fosse l'intestatario di ciascuna. Il risultato era un disastro ordinato: l'account Hetzner era del freelance, il dominio principale era della S.r.l., i certificati Let's Encrypt erano legati al server (quindi al freelance, indirettamente), la zona DNS era su Cloudflare con account intestato al freelance, l'SMTP transazionale era su un account SendGrid sempre intestato al freelance, il repository git era su un GitLab privato self-hosted sullo stesso server. Solo il dominio era davvero della S.r.l. Tutto il resto apparteneva legalmente a una persona fisica che non rispondeva più.

A quel punto ho chiesto al titolare due cose concrete: una PEC urgente inviata all'indirizzo del freelance con oggetto "Richiesta trasferimento intestazione infrastrutture tecniche" e copia per conoscenza allo studio legale aziendale, e una visura camerale aggiornata della S.r.l. da allegare alle richieste di trasferimento presso i provider. La PEC serviva a mettere nero su bianco il tentativo di contatto - fondamentale perché Hetzner richiede prova di assenza di opposizione del titolare dell'account prima di accettare una change of ownership unilaterale. La visura serviva come prova di esistenza del soggetto giuridico richiedente.

Trasferimento di proprietà dell'account Hetzner: la procedura reale con il Robot

Hetzner gestisce i dedicati attraverso un pannello separato chiamato Hetzner Robot, distinto dal Cloud Console. Il trasferimento di intestazione di un server dedicato si fa compilando il form ufficiale di change of ownership, che richiede: dati del vecchio intestatario (nome, email, customer number), dati del nuovo intestatario (ragione sociale, partita IVA, indirizzo sede legale), numero del server da trasferire, e firma congiunta delle due parti. Se la firma congiunta non è possibile - come nel nostro caso, perché il freelance era irreperibile - Hetzner accetta una procedura alternativa che richiede: documento di identità del legale rappresentante del nuovo intestatario, visura camerale recente, dichiarazione sostitutiva di atto notorio in cui il legale rappresentante attesta l'impossibilità di ottenere il consenso del vecchio intestatario e si assume la responsabilità di eventuali contestazioni, e copia della PEC inviata al vecchio intestatario come prova del tentativo di contatto.

La richiesta è partita via ticket supporto Hetzner lunedì 12 maggio nel pomeriggio, con tutti gli allegati scansionati. Hetzner risponde in tedesco e inglese, e il loro team legale è rapido ma pignolo: mi hanno chiesto due chiarimenti nei successivi giorni (copia conforme della visura in inglese, perché l'originale era in italiano, e specifica del regime fiscale B2B intra-UE per la fatturazione). Il trasferimento formale è stato completato venerdì 16 maggio - cinque giorni lavorativi pieni - con riemissione del customer number a nome della S.r.l. e password del Robot resettata via email verso il nuovo indirizzo aziendale. In parallelo, nello stesso periodo, ho gestito lo stesso tipo di richiesta su Cloudflare (procedura Account Takeover Policy documentata nel loro centro assistenza) e su SendGrid, entrambi con tempi simili.

Due cose da sapere per chi si trova in situazioni analoghe. Prima: i provider seri hanno tutti una procedura di change of ownership documentata, ma nessuno la pubblicizza nella documentazione di primo livello - bisogna cercarla espressamente o aprire un ticket. Seconda: la PEC con richiesta di cessione inviata al vecchio intestatario ha un peso legale altissimo in Italia, anche quando si tratta di interlocutori esteri, perché certifica data e contenuto della comunicazione in modo opponibile. Quella PEC era la nostra assicurazione.

Rescue boot e reset della password di root sull'AX51-NVMe

Mentre la pratica Hetzner viaggiava in parallelo, il cliente non poteva aspettare cinque giorni per toccare il server: il certificato Let's Encrypt sarebbe scaduto il 6 giugno e non avevamo nessuna garanzia che il rinnovo automatico funzionasse. Ma senza accesso al Robot non potevo mettere la macchina in rescue mode ufficiale di Hetzner. Fortunatamente, il titolare aveva ancora accesso fisico indiretto alla macchina tramite una vecchia password di un utente non-root che il freelance gli aveva dato due anni prima per scaricare manualmente dei report di vendita - password che non era mai stata ruotata. Da lì, usando sudo solo per leggere i log di audit e capire le abitudini del freelance (non per modificare nulla, finché non ero legalmente proprietario), ho potuto mappare lo stato del sistema senza rischi di accesso abusivo.

Il venerdì 16 maggio, appena arrivata conferma del trasferimento Hetzner, ho immediatamente richiesto attivazione della rescue mode ufficiale e riavvio della macchina. La rescue mode di Hetzner è un sistema Linux live avviato via PXE con accesso SSH come root e password generata al volo, utile proprio per intervenire su macchine a cui non hai più le credenziali native. Una volta dentro il rescue, i comandi per rimontare il filesystem principale e resettare la password di root sono standard:

fdisk -l
mdadm --assemble --scan
mkdir /mnt/rescue
mount /dev/md2 /mnt/rescue
mount --bind /dev /mnt/rescue/dev
mount --bind /proc /mnt/rescue/proc
mount --bind /sys /mnt/rescue/sys
chroot /mnt/rescue /bin/bash
passwd root
exit
umount /mnt/rescue/sys /mnt/rescue/proc /mnt/rescue/dev /mnt/rescue
reboot

Su AX51-NVMe il RAID 1 software è gestito da mdadm su Debian 12, quindi l'assemblaggio degli array è necessario prima del mount. Il chroot dentro il filesystem reale permette di usare il binario passwd del sistema installato (e non quello del rescue), garantendo che l'hash finisca nel /etc/shadow della produzione. Ho ruotato la password di root e, subito dopo, mi sono loggato via SSH dall'esterno sulla macchina con le nuove credenziali e ho aggiunto la mia chiave pubblica in /root/.ssh/authorized_keys per accessi successivi senza dover riusare la password. Il reboot dalla rescue mode dura poco più di un minuto sull'AX51 e, quando la macchina è tornata su, il sito era raggiungibile normalmente: nessun utente finale si è accorto di nulla.

Audit operativo: cosa girava davvero su quella macchina

Con il controllo completo in mano, la domenica 18 maggio ho dedicato l'intera giornata all'audit del server per capire esattamente cosa ci girasse sopra, perché la documentazione era zero e nessuno in azienda sapeva descrivere l'infrastruttura al di là di "c'è Magento e un programma che gira la notte". L'obiettivo era produrre un inventario completo prima di toccare qualsiasi configurazione, seguendo lo stesso metodo che applico in ogni audit tecnico iniziale nei primi 30 giorni di un subentro. Il comando che uso come punto di partenza è questo, salvato in uno script che produce un report completo in meno di due minuti:

set -euo pipefail
REPORT="/root/audit-$(date +%Y%m%d).txt"
{
    echo "=== SISTEMA ==="
    uname -a
    lsb_release -a
    uptime
    echo "=== HARDWARE ==="
    lscpu | grep -E "Model name|CPU\(s\)"
    free -h
    df -hT
    cat /proc/mdstat
    echo "=== SERVIZI ATTIVI ==="
    systemctl list-units --type=service --state=running --no-pager
    echo "=== PORTE IN ASCOLTO ==="
    ss -tlnp
    echo "=== UTENTI CON SHELL ==="
    grep -E "/bash|/sh" /etc/passwd
    echo "=== CRON ==="
    for u in $(cut -d: -f1 /etc/passwd); do
        crontab -u "$u" -l 2>/dev/null && echo "---crontab utente $u---"
    done
    cat /etc/crontab
    ls -la /etc/cron.d /etc/cron.daily /etc/cron.hourly
    echo "=== PACCHETTI INSTALLATI ==="
    dpkg -l | grep -E "^ii" | awk '{print $2, $3}'
    echo "=== WEB SERVER ==="
    nginx -T 2>/dev/null || apache2ctl -S 2>/dev/null
    echo "=== PHP ==="
    php -v
    php -m
    echo "=== DATABASE ==="
    ss -tlnp | grep -E "3306|5432|27017"
} > "$REPORT"

Quello script mi ha rivelato diverse cose interessanti. Primo: il server girava Debian 11 Bullseye, fine supporto LTS ad agosto 2026, quindi con margine di pianificazione per un eventuale aggiornamento a Debian 13 Trixie da fare nei mesi successivi. Secondo: erano installati e attivi Nginx 1.18, PHP-FPM 8.1, MariaDB 10.5, Redis 6.0, un GitLab CE self-hosted sulla porta 8443, e un Portainer sulla porta 9000 che nessuno sapeva esistesse. Terzo, e questo è stato il momento in cui mi sono veramente preoccupato: l'utente git aveva una shell bash attiva, e nel suo crontab girava ogni quindici minuti uno script che sincronizzava il repository Magento verso un bitbucket privato intestato sempre al freelance - una backdoor legittima dal punto di vista operativo, ma potenzialmente anche un vettore di esfiltrazione continua.

Rotazione integrale delle credenziali e chiusura delle backdoor

Il lunedì 19 maggio, con in mano l'inventario completo, ho pianificato la rotazione di tutti i segreti del sistema. Non si trattava di hardening in senso stretto - quella viene dopo, con il mio metodo da checklist di hardening in quattordici giorni - ma di chiudere nell'immediato ogni possibile punto di accesso del vecchio sviluppatore. La logica di priorità era: tutto ciò che dava accesso interattivo alla macchina o ai dati doveva cambiare entro 24 ore, tutto ciò che era internal-only aveva margine fino alla fine della settimana.

In ordine di esecuzione: password SSH di tutti gli utenti con shell, comprese quelle tecniche come git e deploy; chiavi SSH autorizzate in ~/.ssh/authorized_keys di ogni utente (ho fatto backup della vecchia authorized_keys, svuotato il file, aggiunto solo la mia chiave e quella del titolare); password root del database MariaDB e di tutti gli utenti applicativi (con aggiornamento dei file .env di Magento e del gestionale custom); token di accesso di SendGrid (dopo il takeover completato sabato); token Cloudflare API usato dal gestionale per purgare la cache; chiave di deploy del repository git su bitbucket; password admin di GitLab self-hosted e di Portainer; infine, e questo richiede particolare attenzione perché può rompere la posta, credenziali SMTP locali usate per le notifiche di sistema. Ogni credenziale ruotata è finita in un password manager aziendale (Bitwarden self-hosted su un container separato) con accesso del titolare e mio, e segnata in un documento di riconciliazione con data, responsabile e motivo.

La backdoor via git crontab è stata l'ultima cosa che ho toccato. Prima di disattivarla, ho letto lo script per capire cosa facesse davvero - era uno rsync del repository Magento verso un bitbucket esterno, apparentemente innocuo, ma puntava a un repository di cui il titolare non aveva visibilità. L'ho disattivato commentando la riga nel crontab (non cancellando, perché volevo poter ripristinare l'esatto stato precedente in caso di contestazione) e ho documentato la decisione nel report di audit. Il gestionale ha continuato a funzionare senza problemi: quella sync non era nel path critico.

Dopo l'emergenza: runbook, backup, monitoring

Il quinto e sesto giorno li abbiamo dedicati a produrre la documentazione operativa e a impostare il minimo indispensabile di osservabilità, perché un server dedicato "recuperato" senza documentazione e senza monitoring è una bomba a orologeria che esploderà la prossima volta che qualcosa si romperà. Il mio approccio in questi casi è semplice: un runbook di 8-10 pagine in markdown versionato nel repository aziendale, un set di backup automatici verso storage box esterno, e un minimo di monitoring che avvisi via email al titolare quando qualcosa si rompe davvero. Nessuna soluzione enterprise, nessun Prometheus+Grafana+Loki: quelli arrivano dopo, se il progetto cresce. Nei primi giorni serve solo "so dove guardare quando qualcosa va male" e "mi arriva una mail se il disco si riempie o se il sito non risponde".

Il runbook conteneva: architettura sintetica del server (componenti, porte, utenti, path applicativi), inventario dei servizi con descrizione di cosa facciano, procedura di accesso SSH con chiavi, procedura di deploy per Magento e per il gestionale custom, procedura di rinnovo certificati SSL, procedura di backup e ripristino, contatti del supporto Hetzner, contatti dei miei interventi on-call. I backup li ho impostati con BorgBackup verso una Hetzner Storage Box da 1 TB, con retention 7 daily + 4 weekly + 6 monthly e verifica settimanale automatica dell'integrità dell'archivio (borg check). Il monitoring l'ho fatto con un semplice monit locale che controlla nginx, php-fpm, mariadb e lo spazio disco, più un piano di disaster recovery documentato con gli scenari più probabili (disco pieno, servizio crashato, certificato scaduto, host unreachable) e le azioni da intraprendere per ognuno.

Il certificato Let's Encrypt è stato rinnovato manualmente il 20 maggio con certbot renew, sedici giorni prima della scadenza, e il relativo cron è stato verificato e testato con certbot renew --dry-run. Il sito ha continuato a girare senza un minuto di downtime durante tutta la procedura. Il costo totale dell'intervento al cliente è stato contenuto nei limiti di un budget ragionevole per una PMI di quelle dimensioni, e il titolare adesso ha in mano un'infrastruttura funzionante, documentata e legalmente intestata alla sua società.

Cosa ho imparato (di nuovo) sui server dedicati unmanaged senza passaggio di consegne

Questo è il quarto caso in due anni in cui mi capita di recuperare un server dedicato abbandonato da un freelance sparito. La casistica si ripete con variazioni minime, e ogni volta mi convinco di più che il problema non sia tecnico ma contrattuale: nelle PMI italiane, quando si stipula un accordo con uno sviluppatore esterno, quasi mai si inserisce una clausola operativa che specifichi dove vanno intestate le infrastrutture, chi detiene le credenziali in copia sigillata, e quale sia la procedura di handover in caso di interruzione del rapporto. Il risultato è che basta una malattia improvvisa del freelance, un trasferimento all'estero, un conflitto economico non risolto, e l'intera infrastruttura diventa inaccessibile. Ho scritto un approfondimento specifico sul subentro senza lo sviluppatore originario su codice PHP legacy che copre il lato applicativo; questo articolo copre il lato infrastrutturale, ma le due problematiche arrivano sempre insieme.

La differenza tra un recupero che dura sei giorni e uno che dura sei settimane è quasi sempre nei primi tre giorni: mappare la proprietà legale prima di qualsiasi azione, aprire formalmente la procedura di change of ownership col provider appena possibile, non toccare la macchina finché non sei legalmente legittimato a farlo (e se devi toccarla, fallo in sola lettura e documenta tutto), tenere un diario cronologico dell'intervento per poterlo produrre in caso di contestazione futura. Un altro aspetto che molti sottovalutano è che il provider - Hetzner, OVH o Aruba che sia - è tuo alleato in questo processo: le loro procedure di change of ownership esistono proprio perché hanno visto lo stesso scenario migliaia di volte, e se gli presenti documentazione ordinata rispondono in tempi rapidi. OVH ha una procedura simile documentata nel loro manuale cliente, Aruba gestisce la cosa attraverso l'area clienti con richiesta di cambio intestazione dell'account, Contabo via ticket supporto come Hetzner.

Se gestisci un server dedicato o un VPS unmanaged presso Hetzner, OVH, Aruba, Digital Ocean o Contabo e ti trovi improvvisamente senza il supporto dello sviluppatore originario, oppure se vuoi prevenire questo scenario stabilendo fin da subito una struttura di passaggio di consegne sicura e documentata, contattami qui. Posso aiutarti a recuperare rapidamente il controllo legale e tecnico dell'infrastruttura, impostare procedure di backup e monitoring affidabili, e costruire la documentazione operativa necessaria perché la prossima interruzione del rapporto con un tecnico esterno non metta più in crisi il business. Nel mio profilo professionale trovi l'esperienza concreta maturata in questi scenari di emergenza su infrastrutture PHP/Laravel e server Linux in produzione.

Ultima modifica: