Claude per sysadmin: 6 attività server quotidiane
Scopri 6 attività server gestite bene da Claude: log di unità fallite, file systemd, nginx e Compose, con i dati sensibili da non incollare mai.
Claude per amministratori di sistema: prima i consigli, poi l'esecuzione
Claude per gli amministratori di sistema funziona al meglio come strumento di revisione. Incollate un estratto di log, un file di configurazione, un comando che non riconoscete o una stringa di errore e riceverete una spiegazione da verificare prima di modificare il server. Una risposta errata non provoca danni finché non la eseguite. Per questo, mantenere il modello nella fase dei consigli è l'intero modello di sicurezza.
Ogni settimana, su un VPS Linux a noleggio (virtual private server), ricorrono sei attività. Per ognuna trovate uno schema di prompt efficace, il comando che consente di verificare la risposta e il tipo di errore previsto. Nessuna richiede che il modello acceda al server. Potete copiare il contenuto da una scheda del browser o da una finestra sul vostro desktop, perché Claude viene eseguito nativamente su Linux sia come applicazione desktop sia come CLI.
Su un server di produzione l'ordine è importante: leggete la spiegazione, eseguite personalmente il controllo e poi decidete. Su una VM di test l'autonomia è accettabile. Sul server che eroga servizi ai clienti è preferibile la revisione, perché il modello non può vedere lo stato reale su cui basa le proprie ipotesi.
Cosa non devi mai incollare
Tutto ciò che inserisci nel prompt lascia il server. Quattro categorie devono rimanere sul server:
- Chiavi private:
~/.ssh/id_ed25519,/etc/ssh/ssh_host_*_keye qualsiasi chiave TLS (transport layer security) contenuta in/etc/letsencrypt/live/. - File con credenziali:
.env,~/.aws/credentials,/root/.docker/config.jsone le password del database presenti in qualsiasi file o riga di log. - Dati degli account:
/etc/shadowe/etc/gshadow. Per rispondere a una domanda di amministrazione di sistema non serve alcun hash di password. - Qualsiasi dato appartenente ai tuoi utenti: indirizzi email, righe degli ordini, log delle richieste contenenti cookie di sessione o PII (personally identifiable information).
Le chiavi pubbliche possono essere incollate. Le chiavi private no. I due file sono simili a colpo d'occhio, quindi leggi la prima riga prima di copiare: un file la cui prima riga contiene BEGIN OPENSSH PRIVATE KEY non deve mai essere inserito in un prompt. Gestire correttamente il materiale delle chiavi SSH merita dieci minuti dedicati.
Oscura i dati prima di incollarli, invece di affidarti alla capacità di individuare un singolo token in 200 righe:
sudo journalctl -u myapp -n 200 --no-pager | sed -E 's/(token|secret|password|api[_-]?key)[=:][^[:space:]]+/\1=REDACTED/gI'Docker presenta un rischio specifico. docker compose config inserisce i valori .env nell'output che stampa, quindi quell'output contiene un secret anche se il file sul disco non lo conteneva. Usa docker compose config -q, che esegue la convalida senza stampare nulla. Per la policy più ampia su ciò che un agente può vedere, tenere i secret fuori dagli agenti AI tratta gli aspetti relativi all'ambiente.
Attività 1: perché il servizio non è riuscito ad avviarsi?
Inizia con i 2 comandi che contengono la risposta:
systemctl status myapp.service --no-pager
sudo journalctl -u myapp.service -b -n 100 --no-pager -o short-isoIncollali entrambi, insieme al contesto che il modello non può dedurre: distribuzione e versione, ultima modifica effettuata, se il servizio ha mai funzionato e da quanto tempo si è verificato il problema. Chiedi prima di tutto di spiegare il meccanismo.
Ubuntu 24.04.myapp.servicefunzionava correttamente finché non ho modificato l’unità un’ora fa. Eccosystemctl statuse le ultime 100 righe del journal. Qual è la prima riga che indica un errore reale e che cosa significa? Per ora non proporre correzioni.
“Per ora non proporre correzioni” è una parte importante del prompt. I log nascondono il primo errore sotto i tentativi successivi che ha causato. Se chiedi al modello di risolvere il problema, spiegherà l’ultima riga che ha trovato. La riga importante si trova in genere circa 20 righe prima del rumore.
Il risultato può essere una riga come Main PID: 1841 (code=exited, status=203/EXEC). Lo stato di uscita 203/EXEC significa che il kernel non ha potuto eseguire il file indicato in ExecStart: il percorso potrebbe non esistere oppure il file potrebbe esistere ma non essere eseguibile. Una riga #! che indica un interprete non installato produce lo stesso stato. Puoi verificare tutti questi casi con ls -l e head -1.
Modalità di errore: una causa inventata. Se incolli troppo poco contenuto, il modello colma la lacuna con una spiegazione generica, ad esempio “la porta è già in uso”. Chiedi invece: “quale riga del contenuto che ti ho fornito supporta questa conclusione?”. Una causa che nessuno può indicare nel testo è soltanto un’ipotesi.
Attività 2: preparare un'unità systemd o una voce cron
Fornisci tutti i dati necessari al file dell'unità: il comando esatto, l'utente con cui deve essere eseguito, la directory di lavoro, se deve attendere la rete e cosa deve accadere quando termina con un codice diverso da zero. Verifica quindi il risultato prima di abilitare qualsiasi elemento.
sudo systemd-analyze verify /etc/systemd/system/myapp.service
sudo systemctl daemon-reload
sudo systemctl start myapp.service
systemctl status myapp.service --no-pagersystemd-analyze verify analizza il file nello stesso modo di systemd, quindi rileva anche gli errori che possono sfuggire a un controllo manuale. Una direttiva scritta in modo errato produce /etc/systemd/system/myapp.service:7: Unknown key name 'Enviroment' in section 'Service', ignoring.. Un binario mancante produce Command /usr/local/bin/myapp is not executable: No such file or directory. Entrambi restano silenziosi durante daemon-reload, motivo per cui un'unità può essere caricata correttamente e fallire subito quando viene eseguita.
Due errori di stesura si ripetono spesso. Il primo è After=network.target, che indica soltanto che lo stack di rete è configurato, non che esista già un indirizzo. Un servizio che si associa a un IP specifico fallisce quindi durante il boot con bind: Cannot assign requested address; la correzione consiste nell'usare Wants=network-online.target insieme a After=network-online.target. Il secondo è Type=simple per un programma che si demoniizza: systemd considera il primo processo come il servizio, il processo padre termina subito e l'unità viene contrassegnata come inattiva, mentre il processo reale continua a essere eseguito senza gestione. È l'errore che un modello probabilmente ti proporrà, perché dal comando non può capire se il binario crea un processo figlio; prima di accettare la bozza, conviene quindi sapere che cosa garantisce a systemd ciascun valore di Type=.
Per una pianificazione, verifica direttamente il risultato invece di leggerla:
systemd-analyze calendar 'Mon *-*-* 04:00:00'Il comando stampa la forma normalizzata e il momento successivo in cui l'espressione verrà eseguita, eliminando ogni ambiguità sul suo significato. Se devi scegliere tra un timer e un crontab, servizi e timer systemd su un VPS illustra i compromessi.
Cron presenta un problema che nessun modello segnala se non lo chiedi. Cron esegue i job con un ambiente minimo, quindi PATH equivale indicativamente a /usr/bin:/bin e il profilo della shell non viene mai letto. Un job che funziona quando lo incolli nel terminale fallisce con cron producendo /bin/sh: 1: docker: not found, perché il binario si trova in /usr/local/bin. Nei crontab usa percorsi assoluti. Se l'obbligo del file dell'unità di specificare utente, ambiente e dipendenze ti sembra una formalità rispetto a una singola riga crontab, i problemi che systemd è stato progettato per risolvere spiegano l'origine di questa maggiore verbosità.
Job 3: esaminare un file nginx o Compose prima di metterlo in produzione
Questo job offre il miglior ritorno. Incolla il file, descrivi cosa dovrebbe fare e chiedi un'analisi riga per riga di ciò che fa realmente.
Questo vhost dovrebbe pubblicareexample.comtramite HTTPS e inoltrare/apia un servizio locale sulla porta 8080. Rileggilo e indica tutto ciò che non corrisponde a questa descrizione.
Poi esegui lo strumento che verifica la sintassi:
sudo nginx -t
docker compose config -qnginx -t stampa nginx: configuration file /etc/nginx/nginx.conf test is successful oppure indica il file e la riga, come in nginx: [emerg] unknown directive "proxy_pas" in /etc/nginx/conf.d/app.conf:12. docker compose config -q non stampa nulla quando il file è valido e restituisce un messaggio esplicito, come yaml: line 7: did not find expected key, quando l'indentazione non è corretta.
Nessuno dei due strumenti verifica l'intento. Una configurazione che supera nginx -t può comunque inoltrare il traffico alla porta sbagliata oppure mettersi in ascolto su 0.0.0.0 quando era previsto 127.0.0.1. Questo è il limite in cui il modello è utile, ma anche il punto in cui può fallire: se gli chiedi di correggere una direttiva, spesso restituisce l'intero file riscritto e rimuove senza segnalarlo due delle tue direttive. Chiedi le righe modificate e il motivo di ciascuna modifica, quindi applica manualmente le modifiche.
Verifica cosa hai effettivamente esposto:
sudo ss -tulpnSenza sudo vedi i socket in ascolto, ma non i processi che li gestiscono. Se l'output ti sorprende, cosa sono le porte e come Linux le associa è una lettura più breve.
Attività 4: spiega un comando non familiare prima di eseguirlo
Incolla il comando e poni quattro domande: cosa fa ogni flag, cosa scrive, cosa elimina e cosa succede se lo eseguo due volte. L'ultima domanda evita più danni delle altre.
Prendi find /var/log -name '*.gz' -mtime +7 -delete. Una buona risposta spiega che -mtime +7 conta periodi completi di 24 ore e scarta la frazione, quindi seleziona i file più vecchi di almeno otto giorni, non di sette. Spiega inoltre che find valuta l'espressione da sinistra a destra: se sposti -delete prima di -name, elimini tutto ciò che si trova sotto il percorso iniziale. Il secondo punto è riportato come avvertenza nella pagina man di find e ha fatto perdere a molte persone il contenuto del loro /var/log.
Oppure prendi rsync -a --delete /srv/app/ /backup/app/. La barra finale nel percorso sorgente significa «il contenuto di questa directory». Se la ometti, ottieni /backup/app/app/. Se aggiungi --delete, viene rimosso dalla destinazione tutto ciò che manca nella sorgente. Questo è corretto per un mirror, ma può causare un disastro se il percorso sorgente è errato.
Verifica con lo strumento, non con il modello:
rsync -a --delete --dry-run /srv/app/ /backup/app/ | head -20
find /var/log -name '*.gz' -mtime +7Esegui il find senza -delete per ottenere un elenco invece di perdere dati.
Modalità di errore: allucinazione dei flag. Il modello è affidabile con gli strumenti che hanno trent'anni di documentazione, ma è molto meno affidabile con le CLI (interfacce a riga di comando) dei vendor e con i sottocomandi recenti. In questi casi può generare un flag che sembra corretto, ma non esiste. --help risolve il dubbio in un secondo. Anche le virgolette sono un punto debole. Quando un comando contiene un'espressione $(...), leggi come si espande la sostituzione di comando prima dell'esecuzione invece di fidarti della spiegazione.
Job 5: trasforma la cronologia della shell in una procedura operativa
Hai appena trascorso due ore per far funzionare qualcosa. Queste informazioni sono nella cronologia dello scroll e il mese prossimo saranno scomparse.
history 200 > /tmp/session.txtLeggi quel file ed elimina ogni riga che contiene una password, un token o un identificativo del cliente prima di utilizzarlo. La cronologia della shell è uno dei posti più affidabili in cui cercare un secret su un sistema Linux, perché prima o poi tutti ne inseriscono almeno uno direttamente nella riga di comando. Imposta HISTCONTROL=ignorespace nel file ~/.bashrc: in questo modo, un comando digitato con uno spazio iniziale non viene mai scritto nella cronologia.
Il prompt che produce una procedura operativa utilizzabile richiede controlli, non soltanto passaggi:
Questa è una sessione shell che ha portato un sistema Debian 13 appena installato a un'installazione funzionante di Postgres. Documentala come procedura operativa numerata. Usa un comando per ogni passaggio. Dopo ogni passaggio, indica il comando che dimostra che ha avuto esito positivo e descrivi l'aspetto previsto di un output corretto. Indica i passaggi che dipendevano dal mio host specifico.
Modalità di errore: una ricostruzione ordinata. Nella sessione c'è stato un passaggio eseguito erroneamente due volte prima della correzione, ed è proprio quello che il modello tende a eliminare, perché la trascrizione risulta più lineare senza di esso. Confronta la procedura operativa con la cronologia e reinserisci la correzione. Il modello inoltre inventa comandi di verifica plausibili, quindi esegui ogni controllo che include prima di salvare il file. Se la procedura riguarda il primo avvio, confrontala con i primi dieci minuti su un nuovo VPS per evitare di documentare una versione peggiore di un problema già risolto.
Attività 6: trasformare un messaggio di errore in una correzione
Incolla la stringa esatta, il comando che l'ha prodotta e l'unica modifica apportata prima che comparisse. Chiedi di elencare le cause in ordine di probabilità, indicando per ciascuna un comando che la distingua dalle altre. In questo modo la risposta deve essere verificabile.
nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use). Elenca le cause in ordine di probabilità e indicami un comando per ciascuna che la confermi o la escluda.
Per questo errore il meccanismo è chiaro: un altro processo utilizza già la porta 80 e sudo ss -tulpn | grep ':80 ' ne identifica il proprietario. Spesso si tratta di un secondo processo master di nginx rimasto attivo dopo un reload non riuscito, oppure di Apache, installato come dipendenza e avviato dal relativo pacchetto.
Modalità di errore: una correzione che nasconde la causa. chmod 777, --privileged, la disabilitazione di SELinux e l'esecuzione del servizio come root fanno scomparire l'errore. Rifiuta qualsiasi correzione che estenda i permessi finché il modello non ha spiegato perché il permesso più restrittivo non era sufficiente. Questa spiegazione è la risposta effettiva. Una soluzione alternativa rende soltanto silenzioso l'errore.
Cosa sbaglia, in modo prevedibile
- Non può vedere il tuo server. Ogni risposta dipende da ciò che hai incollato e non ti dirà che l'estratto era troppo breve.
- Le versioni creano ambiguità. I nomi dei pacchetti e i flag predefiniti cambiano tra distribuzioni e release, mentre il modello fa una media tra tutte.
- È fluido anche quando sbaglia. Un meccanismo inventato ha esattamente l'aspetto di uno corretto. Per questo ogni causa indicata sopra è accompagnata da un comando che la verifica.
- Perde il filo nelle sessioni lunghe. I fatti riportati all'inizio di una conversazione di due ore smettono di influenzare le risposte nella parte finale.
Quest'ultimo è soprattutto un problema operativo, più che un limite del modello. La soluzione pratica è gestire il contesto in una sessione lunga di Claude Code: sessioni più brevi, una sola attività per sessione.
Inserire l'agente direttamente nel server
Tutto ciò che precede consiste in operazioni di copia e incolla, quindi il modello non interagisce mai con la macchina. Quando viene eseguito sul server, legge file ed esegue comandi. In questo caso il profilo di rischio cambia: un comando errato può causare l'interruzione di un servizio. Assegnategli un utente non privilegiato dedicato invece di root, non eseguitelo sul server di produzione mentre ne valutate il comportamento e create prima uno snapshot. Eseguire Claude Code in sicurezza su un VPS descrive il sandboxing e il modello delle autorizzazioni. Eseguire Claude Code all'interno di tmux risolve l'altro problema: la perdita della sessione SSH (secure shell) interrompe un agente in primo piano mentre sta eseguendo il lavoro. Create l'account come fareste con qualsiasi account di servizio. La procedura è descritta in utenti con privilegi minimi su un VPS.
FAQ
Claude può leggere direttamente i log del mio server?
Non autonomamente. L’interfaccia di chat vede solo il testo che incolli. Claude Code, eseguito sul server come strumento da riga di comando, può leggere file ed eseguire comandi con i permessi dell’utente che lo ha avviato: si tratta quindi di una decisione che richiede un livello di fiducia maggiore. Per una normale richiesta di supporto, incollare un estratto di 100 righe con i dati sensibili rimossi è più rapido e sicuro che concedere a un agente l’accesso alla shell.
Cosa non devo mai incollare da un server?
Chiavi private, file .env e altri archivi di credenziali, /etc/shadow e qualsiasi dato appartenente ai tuoi utenti. Rimuovi i token dagli estratti dei log prima di inserirli nel prompt. Un caso meno evidente: l’output di docker compose config contiene i valori .env interpolati, quindi usa docker compose config -q, che valida il file senza stampare nulla.
È sicuro lasciare che Claude esegua comandi su un VPS di produzione?
Trattalo come un nuovo amministratore privo di contesto: va bene per leggere, ma le operazioni di scrittura richiedono una revisione. In produzione, chiedi una spiegazione ed esegui tu il comando. Se vuoi comunque che sia un agente a eseguire i comandi, assegnagli un account dedicato senza privilegi, senza accesso sudo generalizzato, e inizia su un sistema di staging, dove un errore comporta una nuova installazione invece di un’interruzione del servizio.
Perché Claude suggerisce un flag che non esiste?
Perché genera testo plausibile, e un flag plausibile ha lo stesso aspetto di uno reale. Succede soprattutto con le CLI dei vendor e con i subcomandi più recenti, per i quali la documentazione disponibile per il modello è limitata o nel frattempo è cambiata. --help e man fanno fede, e qualsiasi comando che elimina o sovrascrive dati deve essere preceduto da una dry run.
Come posso controllare un'unità systemd prima di abilitarla?
Esegui sudo systemd-analyze verify /etc/systemd/system/myapp.service. Analizza il file con il parser di systemd, segnala le direttive sconosciute con il relativo numero di riga e indica un binario ExecStart mancante o non eseguibile. Esegui quindi daemon-reload, start e leggi systemctl status prima di enable l’unità, perché un’unità che viene caricata correttamente può comunque non riuscire alla prima esecuzione.