0Pricing
DevOps Bootcamp · Lezione

File temporanei sicuri e directory di lock

Usi mktemp e flock per creare risorse temporanee senza race condition e impedire l'esecuzione simultanea degli script.

File temporanei sicuri e directory di lock è una lezione DevOps Bootcamp gratuita su CoddyKit. Questa è la lezione 3 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento DevOps Bootcamp, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso DevOps Bootcamp include 4 lezioni in totale.

Perché i file temporanei sono un rischio per la sicurezza

Gli script Bash hanno spesso bisogno di spazio temporaneo per risultati intermedi, indicatori di lock e aree di staging. Tuttavia, creare file temporanei senza le dovute precauzioni espone a gravi vulnerabilità.

  • Race condition: un altro processo può prevedere il nome del file e crearlo per primo, reindirizzando le scritture.
  • Attacchi tramite symlink: un attaccante crea un symlink nel percorso previsto, indirizzandolo a un file sensibile come /etc/passwd.
  • File residui: se uno script va in crash, i file temporanei si accumulano e possono esporre dati sensibili.

I due strumenti fondamentali che eliminano questi problemi sono mktemp e flock. In questa lezione imparerà a usare entrambi in modo sicuro e difensivo.

Creare file temporanei sicuri con mktemp

mktemp crea un file temporaneo con un nome casuale e imprevedibile e ne restituisce il percorso. Crea il file in modo atomico, impedendo a qualsiasi altro processo di appropriarsi prima del nome.

  • Sintassi: mktemp [TEMPLATE] — il modello deve terminare con almeno tre caratteri X.
  • Ogni X viene sostituita da un carattere casuale, producendo un nome univoco come /tmp/script.aB3kQz.
  • Il file viene creato automaticamente con permessi 0600 (leggibile solo dal proprietario).

Acquisisca sempre immediatamente il percorso restituito in una variabile, così da poterlo usare e rimuovere in seguito.

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

# Create a secure temp file
TMPFILE=$(mktemp /tmp/myapp.XXXXXX)
echo "Temp file created at: $TMPFILE"

# Write data to it
echo "some intermediate result" > "$TMPFILE"

# Read it back
cat "$TMPFILE"

# Clean up
rm -f "$TMPFILE"

Eseguire sempre la pulizia con un trap

Se lo script termina in modo imprevisto — a causa di un errore, di un segnale o dell'attivazione di set -e — i file temporanei rimarranno, a meno che non registri un gestore di pulizia.

Il builtin trap esegue un comando quando la shell riceve un segnale o termina. Lo schema canonico per la pulizia dei file temporanei è:

  • Registri il trap subito dopo aver creato il file temporaneo.
  • Imposti il trap su EXIT, affinché la pulizia venga eseguita sia in caso di uscita normale sia anomala.
  • Imposti inoltre il trap su INT e TERM se lo script ha un'esecuzione lunga o è interattivo.

In questo modo non rimarranno file orfani, anche se lo script viene terminato durante l'esecuzione.

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

TMPFILE=$(mktemp /tmp/report.XXXXXX)

# Register cleanup before doing any real work
cleanup() {
    rm -f "$TMPFILE"
    echo "Cleaned up $TMPFILE" >&2
}
trap cleanup EXIT

# Do work — even if this fails, cleanup() will run
echo "Processing..." > "$TMPFILE"
grep "result" "$TMPFILE" || true

echo "Done. File will be removed on exit."

Creazione di directory temporanee con mktemp -d

A volte è necessaria un’intera directory per preparare più file, ad esempio per creare un archivio o estrarre un tarball prima dell’elaborazione. Utilizzi mktemp -d per creare una directory temporanea sicura.

  • La directory viene creata con i permessi 0700 (accesso esclusivo al proprietario).
  • Esegua la pulizia con rm -rf nel proprio trap: presti attenzione a rimuovere solo la variabile, mai un percorso codificato direttamente.
  • Utilizzi le virgolette doppie e verifichi che la variabile non sia vuota prima di chiamare rm -rf, come ulteriore controllo di sicurezza.
#!/usr/bin/env bash
set -euo pipefail

TMPDIR=$(mktemp -d /tmp/extract.XXXXXX)

cleanup() {
    # Guard: only rm if variable is set and non-empty
    [[ -n "${TMPDIR:-}" ]] && rm -rf "$TMPDIR"
}
trap cleanup EXIT

echo "Working in $TMPDIR"

# Simulate staging files
echo "file one" > "$TMPDIR/part1.txt"
echo "file two" > "$TMPDIR/part2.txt"

ls "$TMPDIR"
echo "All done."

Il problema delle esecuzioni simultanee degli script

I cron job, i timer di systemd e gli script avviati manualmente possono facilmente eseguire contemporaneamente più istanze dello stesso script. Questo causa:

  • Elaborazione duplicata: gli stessi record del database o gli stessi file vengono elaborati due volte.
  • Output corrotto: due istanze scrivono contemporaneamente nello stesso file di output.
  • Deadlock o stato parziale: entrambe le istanze modificano risorse condivise in un ordine intercalato e imprevedibile.

La soluzione tradizionale consisteva nello scrivere un file PID e controllarlo all’avvio, ma questo approccio presenta una finestra di race condition tra il controllo e la scrittura. La soluzione moderna corretta è flock, che utilizza il meccanismo di locking consultivo del kernel per garantire un lock privo di race condition.

Locking con flock: il pattern su una riga

flock acquisisce un lock consultivo su un file descriptor prima di eseguire un comando. L’utilizzo più semplice consiste nell’avvolgere l’intero script dalla riga di comando:

flock -n /var/lock/myscript.lock bash myscript.sh

  • -n (non bloccante): termina immediatamente con stato 1 se il lock è già acquisito, invece di attendere.
  • Senza -n, flock resta in attesa finché il lock non diventa disponibile: utile per mettere le esecuzioni in coda.
  • Il file di lock è soltanto un indicatore; il suo contenuto non è importante. È sicuro conservarlo tra un’esecuzione e l’altra.
  • Quando il processo che detiene il lock termina, il kernel lo rilascia automaticamente: non è necessaria alcuna pulizia manuale.
#!/usr/bin/env bash
# launcher.sh — prevents concurrent runs of worker.sh
set -euo pipefail

LOCKFILE="/tmp/myworker.lock"

if ! flock -n "$LOCKFILE" bash -c 'echo "Running worker..."; sleep 2; echo "Done."'; then
    echo "Another instance is already running. Exiting." >&2
    exit 1
fi

flock all’interno di uno script tramite un file descriptor

Per applicare il locking all’interno di uno script invece di avvolgerlo dall’esterno, utilizzi exec per aprire un file descriptor e chiami quindi flock su quel descriptor. Questo è il pattern idiomatico utilizzato negli script di produzione.

  • exec 200>"$LOCKFILE" apre il file sul descriptor 200 in scrittura (creandolo se necessario).
  • flock -n 200 prova ad acquisire senza blocco il lock sul descriptor 200.
  • Poiché il lock è associato al file descriptor, non al nome del file, viene rilasciato automaticamente quando il processo della shell termina.
  • Per convenzione, i numeri di descriptor da 200 a 299 vengono utilizzati per evitare conflitti con stdin/stdout/stderr.
#!/usr/bin/env bash
set -euo pipefail

LOCKFILE="/tmp/myjob.lock"

# Open lock file on FD 200
exec 200>"$LOCKFILE"

# Attempt non-blocking lock
if ! flock -n 200; then
    echo "ERROR: Another instance of this script is running." >&2
    exit 1
fi

echo "Lock acquired. Starting work..."
sleep 1
echo "Work complete. Lock will be released on exit."

Combinare mktemp e flock in un unico script

Gli script realmente difensivi hanno bisogno di entrambi: un lock per impedire le esecuzioni simultanee e file temporanei sicuri per i dati intermedi. Ecco il pattern completo che combina entrambe le tecniche:

  • Acquisisca prima il lock, prima di creare qualsiasi file temporaneo, in modo che una sola istanza esegua il lavoro.
  • Crei le risorse temporanee dopo aver verificato che il lock sia stato acquisito.
  • Registri il trap immediatamente dopo aver creato le risorse temporanee, così la pulizia è garantita indipendentemente dal modo in cui termina lo script.
  • Il file di lock non deve mai trovarsi nella directory temporanea: deve rimanere tra un’esecuzione e l’altra affinché flock possa farvi riferimento.
#!/usr/bin/env bash
set -euo pipefail

LOCKFILE="/tmp/report_builder.lock"
exec 200>"$LOCKFILE"

if ! flock -n 200; then
    echo "Already running — aborting." >&2
    exit 1
fi

# Now safe to create temp resources
TMPDIR=$(mktemp -d /tmp/report.XXXXXX)
TMPLOG=$(mktemp /tmp/report_log.XXXXXX)

cleanup() {
    rm -rf "${TMPDIR:-}"
    rm -f  "${TMPLOG:-}"
}
trap cleanup EXIT

echo "Building report in $TMPDIR" | tee "$TMPLOG"
echo "Step 1 complete"             >> "$TMPLOG"
cat "$TMPLOG"

Le directory di lock come meccanismo alternativo

Nei sistemi in cui flock non è disponibile, ad esempio in alcuni sistemi embedded o nei filesystem di rete come NFS, è possibile utilizzare le directory di lock. Sui sistemi POSIX, mkdir è atomico: ha esito positivo solo se la directory non esiste già.

  • Crei la directory di lock con mkdir /tmp/myscript.lock.d: se un’altra istanza l’ha già creata, mkdir fallisce immediatamente.
  • Memorizzi nella directory i metadati, come il PID, per facilitare la diagnostica.
  • Rimuova sempre la directory in un trap su EXIT.
  • Attenzione: a differenza di flock, una directory di lock NON viene rilasciata automaticamente se il processo viene terminato con -9 o se la macchina viene riavviata: aggiunga un controllo per rilevare i lock obsoleti.
#!/usr/bin/env bash
set -euo pipefail

LOCKDIR="/tmp/myscript.lock.d"

# Atomic mkdir — fails if directory already exists
if ! mkdir "$LOCKDIR" 2>/dev/null; then
    # Check if the holding PID is still alive
    HOLDER_PID=$(cat "$LOCKDIR/pid" 2>/dev/null || echo "")
    if [[ -n "$HOLDER_PID" ]] && kill -0 "$HOLDER_PID" 2>/dev/null; then
        echo "Locked by PID $HOLDER_PID. Exiting." >&2
        exit 1
    else
        echo "Stale lock detected. Removing and continuing." >&2
        rm -rf "$LOCKDIR"
        mkdir "$LOCKDIR"
    fi
fi

echo "$$" > "$LOCKDIR/pid"
trap 'rm -rf "$LOCKDIR"' EXIT

echo "Lock acquired via directory. Running..."
sleep 1
echo "Done."

Attesa basata sul timeout con flock

A volte è necessario attendere un lock invece di fallire immediatamente, ma senza aspettare all’infinito. flock supporta un timeout tramite il flag -w.

  • flock -w 10 200 attende il lock per un massimo di 10 secondi, quindi termina con stato 1 se non è ancora disponibile.
  • È ideale per gli script che devono accodarsi dietro a un’esecuzione precedente di breve durata, ma rinunciare se quest’ultima è bloccata.
  • Combini -w con un messaggio di errore significativo che includa il contesto, ovvero il percorso del file di lock e la durata dell’attesa, così gli operatori possono diagnosticare rapidamente i blocchi.
#!/usr/bin/env bash
set -euo pipefail

LOCKFILE="/tmp/data_sync.lock"
TIMEOUT=15

exec 200>"$LOCKFILE"

echo "Waiting up to ${TIMEOUT}s for lock on $LOCKFILE..."

if ! flock -w "$TIMEOUT" 200; then
    echo "ERROR: Could not acquire lock after ${TIMEOUT}s." \
         "Another instance may be hung." >&2
    exit 1
fi

echo "Lock acquired. Syncing data..."
sleep 1
echo "Sync complete."

Checklist difensiva: risorse temporanee sicure

Prima di distribuire uno script che utilizza file temporanei o locking, verifichi i seguenti punti:

  • Utilizzi mktemp, mai percorsi codificati direttamente: /tmp/myapp.tmp è prevedibile e può essere sfruttato.
  • Salvi immediatamente il percorso: TMPFILE=$(mktemp ...) prima di qualsiasi altro comando.
  • Registri trap cleanup EXIT immediatamente dopo la creazione: non alla fine dello script.
  • : rm -f "$TMPFILE", mai rm -f $TMPFILE.
  • Preferisca flock ai file PID: è gestito dal kernel e viene rilasciato automaticamente in caso di crash.
  • Utilizzi -n non bloccante come impostazione predefinita: i lock bloccanti e silenziosi nascondono i problemi di prestazioni.
  • Collochi il file di lock fuori dalla directory temporanea: così sopravvive al trap di pulizia.
  • Verifichi il comportamento della pulizia: esegua lo script e utilizzi kill -9 durante l’esecuzione; verifichi che non rimangano file residui (per gli script basati su flock; le directory di lock richiedono maggiore attenzione).

Verifica delle conoscenze: comportamento dei flag di flock

Un cron job viene eseguito ogni minuto ed elabora un file condiviso. Si desidera che ogni nuova invocazione termini immediatamente con un errore se un’esecuzione precedente è ancora attiva, senza attendere. Quale invocazione di flock implementa correttamente questo comportamento?

Riepilogo: file temporanei sicuri e directory di lock

In questa lezione ha appreso i due strumenti essenziali per la gestione difensiva delle risorse in Bash:

  • mktemp crea file temporanei con nomi imprevedibili e permessi sicuri (0600) e directory temporanee (0700), eliminando le race condition e gli attacchi tramite link simbolici che affliggono i percorsi codificati direttamente.
  • trap cleanup EXIT garantisce la rimozione dei file temporanei a ogni terminazione, normale, dovuta a un errore o a un segnale, quando viene registrato immediatamente dopo la creazione.
  • flock fornisce un locking consultivo applicato dal kernel: utilizzi -n per fallire rapidamente in caso di contesa, -w N per attendere con un timeout e il pattern exec 200>file per il locking all’interno dello script, che il kernel rilascia automaticamente alla terminazione del processo.
  • Le directory di lock (mkdir) offrono un’alternativa portabile negli ambienti in cui flock non è disponibile, ma richiedono un controllo esplicito dei lock obsoleti.
  • Mantenga sempre il file di lock fuori dalla directory temporanea e racchiuda tra virgolette doppie ogni variabile utilizzata nella pulizia.

La combinazione di mktemp + flock + trap consente di creare script sicuri contro invocazioni simultanee, crash imprevedibili e manipolazioni malevole del filesystem.

Domande Frequenti

La lezione «File temporanei sicuri e directory di lock» è gratuita?

Sì — il testo completo di «File temporanei sicuri e directory di lock» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso DevOps Bootcamp, passa a CoddyKit PRO. Il corso DevOps Bootcamp include 4 lezioni in totale.

Cosa imparerò in «File temporanei sicuri e directory di lock»?

Usi mktemp e flock per creare risorse temporanee senza race condition e impedire l'esecuzione simultanea degli script. Eserciti DevOps Bootcamp con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare DevOps Bootcamp?

Non è richiesta alcuna esperienza precedente. DevOps Bootcamp su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 3 di 4.

Quanto tempo richiede la lezione «File temporanei sicuri e directory di lock»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione DevOps Bootcamp?

Sì. Ogni lezione DevOps Bootcamp include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Modalità rigorosa con set -euo pipefail
  2. Gestori trap per pulizia e segnali
  3. File temporanei sicuri e directory di lock
  4. Script idempotenti e logica di retry con backoff
← Torna a DevOps Bootcamp