0Pricing
Linux Command Line & Bash Scripting Mastery · Lezione

Automatizzare la gestione di utenti e gruppi

Crei, modifichi e verifichi account in blocco usando useradd, chage e la gestione di frammenti sudoers.

Automatizzare la gestione di utenti e gruppi è una lezione Linux Command Line & Bash Scripting Mastery gratuita su CoddyKit. Questa è la lezione 1 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 Linux Command Line & Bash Scripting Mastery, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Linux Command Line & Bash Scripting Mastery include 4 lezioni in totale.

Perché automatizzare il provisioning degli utenti?

Gestire gli utenti uno alla volta con useradd funziona bene per pochi account, ma negli ambienti aziendali vengono abitualmente attivati contemporaneamente decine o centinaia di utenti. I comandi manuali diventano soggetti a errori, incoerenti e non verificabili.

Lo scripting Bash consente di:

  • Creare ogni volta utenti con impostazioni standard (shell, directory home, criteri delle password)
  • Leggere un file CSV o di testo contenente i nuovi assunti e attivarli con una sola esecuzione
  • Registrare ogni azione per disporre di una traccia di audit ai fini della conformità
  • Integrare il processo con pipeline di gestione della configurazione (Ansible, Chef, Jenkins)

Questa lezione illustra la creazione da zero di uno script di provisioning degli utenti adatto alla produzione, trattando useradd, chage, usermod, la gestione dei gruppi, i file drop-in di sudoers e l'audit successivo all'esecuzione.

Lettura di un elenco di utenti in blocco

Il formato di input canonico per il provisioning in blocco è un file di testo delimitato, con un record per riga. Un tipico CSV potrebbe avere questo aspetto:

username,full_name,group,shell
alice,Alice Smith,developers,/bin/bash
bob,Bob Jones,ops,/bin/zsh

Utilizzi IFS e read all'interno di un ciclo while per analizzarlo in modo sicuro. Saltare la riga di intestazione con tail -n +2 mantiene pulita la logica.

Principali pratiche di difesa:

  • Rimuovere gli spazi iniziali e finali da ogni campo
  • Saltare le righe vuote e quelle di commento che iniziano con #
  • Verificare che i campi obbligatori non siano vuoti prima di chiamare qualsiasi comando di sistema
#!/usr/bin/env bash
# parse_users.sh — safely read a CSV of users
set -euo pipefail

USER_FILE="${1:-users.csv}"

[[ -f "$USER_FILE" ]] || { echo "ERROR: $USER_FILE not found"; exit 1; }

tail -n +2 "$USER_FILE" | while IFS=',' read -r username full_name group shell; do
  # trim whitespace
  username="${username// /}"
  [[ -z "$username" || "$username" == \#* ]] && continue

  echo "Parsed -> user=$username group=$group shell=$shell"
done

Creazione di utenti con useradd

useradd è l'utility di basso livello che scrive in /etc/passwd, /etc/shadow e /etc/group. Le opzioni più importanti per lo scripting sono:

  • -m — crea la directory home
  • -s — imposta la shell di login
  • -c — campo commento GECOS (nome completo)
  • -G — gruppi supplementari (separati da virgole)
  • -e — data di scadenza dell'account (AAAA-MM-GG)

Verifichi sempre se l'utente esiste già con id prima di chiamare useradd; eseguirlo su un utente esistente restituisce il codice di uscita 9 e stampa un errore che potrebbe appesantire i log.

Nota: useradd richiede i privilegi di root. Inserisca all'inizio dello script un controllo dei privilegi.

#!/usr/bin/env bash
# create_user.sh — idempotent single-user creation
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USERNAME="$1"
FULL_NAME="${2:-}"
GROUP="${3:-staff}"
SHELL="${4:-/bin/bash}"

if id "$USERNAME" &>/dev/null; then
  echo "[SKIP] User $USERNAME already exists"
else
  useradd \
    --create-home \
    --shell "$SHELL" \
    --comment "$FULL_NAME" \
    --groups "$GROUP" \
    "$USERNAME"
  echo "[OK] Created $USERNAME"
fi

Impostazione sicura delle password iniziali

Non inserisca mai password in chiaro negli script. Due approcci sicuri per il provisioning in blocco sono:

  • Generare una password iniziale casuale con openssl rand o /dev/urandom, visualizzarla una sola volta e obbligare l'utente a modificarla al primo accesso
  • Impostare una password pre-hashata usando usermod -p con un hash SHA-512, in modo che il testo in chiaro non compaia nell'elenco dei processi

chpasswd è lo strumento consigliato per impostare le password tramite script: legge da stdin coppie username:password, così la password non compare negli argomenti della riga di comando (visibili tramite ps).

Dopo aver impostato la password, utilizzi chage -d 0 per obbligare l'utente a reimpostarla al successivo accesso.

#!/usr/bin/env bash
# set_temp_password.sh
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USERNAME="$1"

# Generate a 16-char random password (alphanumeric only)
TMP_PASS=$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 16)

# Set password via chpasswd (password never in argv)
echo "${USERNAME}:${TMP_PASS}" | chpasswd

# Force password change on next login
chage -d 0 "$USERNAME"

echo "[OK] Temporary password for $USERNAME: $TMP_PASS"
echo "[OK] User must change password on first login"

Gestione della scadenza delle password con chage

chage (change age) controlla i criteri di scadenza delle password memorizzati in /etc/shadow. I criteri di sicurezza aziendali impongono generalmente:

  • Durata massima della password (ad esempio, 90 giorni)
  • Numero minimo di giorni prima di poter modificare nuovamente una password
  • Periodo di avviso prima della scadenza
  • Blocco dell'account dopo un periodo di inattività successivo all'ultimo utilizzo della password

Opzioni principali di chage:

  • -M <days> — durata massima della password
  • -m <days> — durata minima della password
  • -W <days> — giorni di avviso prima della scadenza
  • -I <days> — giorni di inattività prima del blocco dell'account
  • -E <date> — scadenza assoluta dell'account
  • -l — elenca le impostazioni correnti di un utente
#!/usr/bin/env bash
# apply_password_policy.sh — enforce org-wide ageing policy
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

# Policy constants
MAX_AGE=90
MIN_AGE=1
WARN_DAYS=14
INACTIVE_DAYS=30

apply_policy() {
  local user="$1"
  chage \
    -M "$MAX_AGE" \
    -m "$MIN_AGE" \
    -W "$WARN_DAYS" \
    -I "$INACTIVE_DAYS" \
    "$user"
  echo "[OK] Policy applied to $user"
}

# Apply to all non-system users (UID >= 1000)
awk -F: '$3 >= 1000 && $3 < 65534 { print $1 }' /etc/passwd | while read -r user; do
  apply_policy "$user"
done

Gestione dei gruppi in blocco

I gruppi sono il meccanismo principale per controllare l'accesso alle risorse. Uno script di provisioning deve assicurarsi che i gruppi necessari esistano prima di aggiungervi gli utenti: useradd -G nonexistent fallirà.

Utilizzi groupadd in modo idempotente verificando il codice di uscita: restituisce 9 se il gruppo esiste già. L'idioma getent group <name> è un'alternativa portabile e leggibile alla ricerca in /etc/group con grep.

gpasswd -a aggiunge un utente a un gruppo senza sostituire le appartenenze esistenti, a differenza di usermod -G, che sostituisce l'elenco dei gruppi supplementari.

#!/usr/bin/env bash
# ensure_groups.sh — create groups if missing, then add users
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

REQUIRED_GROUPS=(developers ops security auditors)

for grp in "${REQUIRED_GROUPS[@]}"; do
  if getent group "$grp" &>/dev/null; then
    echo "[SKIP] Group $grp already exists"
  else
    groupadd "$grp"
    echo "[OK] Created group $grp"
  fi
done

# Safely add a user to a group (append, don't replace)
add_to_group() {
  local user="$1" group="$2"
  gpasswd -a "$user" "$group" 2>/dev/null && echo "[OK] $user -> $group"
}

Script completo per il provisioning in blocco

Riunendo tutti gli elementi: un singolo script legge un CSV, crea utenti e gruppi, imposta i criteri delle password, registra ogni azione e gestisce gli errori in modo appropriato senza interrompere l'intero batch.

Scelte progettuali importanti nello script seguente:

  • Un LOG_FILE con timestamp registra tutte le operazioni a fini di audit
  • Gli errori relativi ai singoli utenti vengono registrati ma non interrompono il ciclo (|| log_error)
  • Lo script è idempotente, quindi può essere eseguito di nuovo in sicurezza dopo errori parziali
  • Lo stesso output viene inviato sia al terminale sia al file di log tramite tee
#!/usr/bin/env bash
# bulk_provision.sh — production user provisioning
set -uo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USER_FILE="${1:-users.csv}"
LOG_FILE="/var/log/user_provision_$(date +%F).log"

log()  { echo "[$(date '+%F %T')] $*" | tee -a "$LOG_FILE"; }
err()  { log "ERROR: $*"; }

log "=== Provisioning started from $USER_FILE ==="

tail -n +2 "$USER_FILE" | while IFS=',' read -r username fullname group shell; do
  username="${username// /}"
  [[ -z "$username" || "$username" == \#* ]] && continue
  group="${group:-staff}"
  shell="${shell:-/bin/bash}"

  # Ensure group exists
  getent group "$group" &>/dev/null || groupadd "$group"

  # Create user idempotently
  if id "$username" &>/dev/null; then
    log "[SKIP] $username exists"
  else
    useradd -m -s "$shell" -c "$fullname" -G "$group" "$username" || { err "useradd failed for $username"; continue; }
    TMP="$(tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 14)"
    echo "${username}:${TMP}" | chpasswd
    chage -M 90 -m 1 -W 14 -I 30 -d 0 "$username"
    log "[OK] $username created (group=$group) tmp_pass=$TMP"
  fi
done

log "=== Provisioning complete ==="

Gestione dei frammenti drop-in di sudoers

Modificare direttamente /etc/sudoers è rischioso: un errore di sintassi impedisce a tutti di usare sudo. L'approccio sicuro consiste nell'utilizzare file drop-in in /etc/sudoers.d/, verificando ciascuno con visudo -c -f prima di installarlo.

Buone pratiche per i frammenti di sudoers:

  • Assegni ai file il nome del team o del ruolo a cui concedono l'accesso (ad esempio, 10-developers, 20-ops)
  • Utilizzi regole basate sui gruppi (%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl) anziché righe per singolo utente
  • Imposti sempre i permessi su 0440 e la proprietà su root:root
  • Verifichi con visudo -c: restituisce un codice diverso da zero in presenza di qualsiasi errore di sintassi
#!/usr/bin/env bash
# write_sudoers_fragment.sh
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

FRAGMENT_NAME="${1:-10-developers}"
SUDOERS_DIR="/etc/sudoers.d"
TMP_FILE="$(mktemp)"

# Write the fragment to a temp file first
cat > "$TMP_FILE" << 'EOF'
# Developers: restart services and view journals without full root
%developers ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart *, /usr/bin/journalctl
%ops        ALL=(ALL) NOPASSWD: ALL
EOF

# Validate BEFORE installing
if visudo -c -f "$TMP_FILE"; then
  install -m 0440 -o root -g root "$TMP_FILE" "${SUDOERS_DIR}/${FRAGMENT_NAME}"
  echo "[OK] Installed ${SUDOERS_DIR}/${FRAGMENT_NAME}"
else
  echo "[ERROR] sudoers syntax check failed — fragment NOT installed"
  rm -f "$TMP_FILE"
  exit 1
fi
rm -f "$TMP_FILE"

Audit degli account esistenti

Dopo il provisioning, e secondo una pianificazione regolare, dovrebbe eseguire l'audit del database degli utenti per individuare anomalie:

  • Account con UID 0 — qualsiasi account con UID 0 diverso da root costituisce una criticità di sicurezza
  • Account senza password — voci con un campo password vuoto o con ! in /etc/shadow
  • Account scaduti ancora attivi — l'output di chage -l può essere analizzato in blocco
  • Utenti con shell ma senza directory home — una configurazione errata che impedisce l'accesso

Generare un report strutturato e inviarlo via email al team di sicurezza è semplice con mail oppure aggiungendolo a un percorso di log monitorato.

#!/usr/bin/env bash
# audit_users.sh — produce a security-relevant user report
set -uo pipefail

REPORT="/var/log/user_audit_$(date +%F).txt"

echo "=== User Audit Report $(date) ===" > "$REPORT"

echo "" >> "$REPORT"
echo "--- Accounts with UID 0 (should be root only) ---" >> "$REPORT"
awk -F: '$3 == 0 { print $1 }' /etc/passwd >> "$REPORT"

echo "" >> "$REPORT"
echo "--- Accounts with empty password field ---" >> "$REPORT"
awk -F: '($2 == "" || $2 == "!") && $3 >= 1000 { print $1 }' /etc/shadow 2>/dev/null >> "$REPORT" || echo "  (requires root)" >> "$REPORT"

echo "" >> "$REPORT"
echo "--- Normal users (UID 1000-60000) ---" >> "$REPORT"
awk -F: '$3 >= 1000 && $3 < 60000 { printf "%-20s uid=%-6s shell=%s\n", $1, $3, $7 }' /etc/passwd >> "$REPORT"

cat "$REPORT"
echo "Report saved to $REPORT"

Blocco, sblocco e rimozione degli account

La disattivazione degli utenti è importante quanto la loro attivazione. Quando un utente lascia l'organizzazione, la sequenza corretta è:

  1. Bloccare immediatamente l'account (usermod -L): antepone ! all'hash della password in shadow, impedendo l'accesso senza eliminare i dati
  2. Revocare sudo: rimuovere il relativo frammento di sudoers, se presente
  3. Trasferire la proprietà dei suoi file a un responsabile o a un account di archiviazione
  4. Archiviare la directory home in un tarball prima di eliminarla
  5. Eliminare con userdel -r: rimuove la home e la coda di posta

usermod -U sblocca un account (rimuove il prefisso !), una funzione utile per le sospensioni temporanee.

#!/usr/bin/env bash
# offboard_user.sh — lock, archive, then optionally delete
set -euo pipefail

[[ $EUID -ne 0 ]] && { echo "Must run as root"; exit 1; }

USERNAME="$1"
ARCHIVE_DIR="/srv/archived-homes"
mkdir -p "$ARCHIVE_DIR"

# 1. Lock account
usermod -L "$USERNAME"
echo "[OK] Account $USERNAME locked"

# 2. Remove sudoers fragment if present
SUDOERS_FILE="/etc/sudoers.d/${USERNAME}"
[[ -f "$SUDOERS_FILE" ]] && rm -f "$SUDOERS_FILE" && echo "[OK] Removed sudoers fragment"

# 3. Archive home directory
HOME_DIR="$(getent passwd "$USERNAME" | cut -d: -f6)"
if [[ -d "$HOME_DIR" ]]; then
  tar -czf "${ARCHIVE_DIR}/${USERNAME}_$(date +%F).tar.gz" -C "$(dirname "$HOME_DIR")" "$(basename "$HOME_DIR")"
  echo "[OK] Home archived to ${ARCHIVE_DIR}/${USERNAME}_$(date +%F).tar.gz"
fi

echo "[NOTICE] Review archive, then run: userdel -r $USERNAME"

Test dello script con una modalità dry-run

Gli script di provisioning destinati alla produzione devono poter essere testati senza effetti collaterali. Implementi una modalità dry-run usando un flag DRY_RUN che sostituisce tutti i comandi che modificano il sistema con stub echo.

Il modello è semplice: definisca un helper run() che, in base al flag, esegua il comando oppure lo visualizzi. Questo approccio consente di:

  • Eseguire ogni percorso del codice durante i test
  • Mostrare nell'output esattamente ciò che accadrebbe in un'esecuzione reale
  • Consentire alle pipeline CI di verificare la logica senza accesso root

Completi il dry-run con un prefisso dedicato per gli utenti di test (ad esempio, test_) che semplifichi la pulizia dopo i test di integrazione.

#!/usr/bin/env bash
# provision_with_dryrun.sh
set -euo pipefail

DRY_RUN="${DRY_RUN:-false}"

# Wrapper: execute or echo
run() {
  if [[ "$DRY_RUN" == "true" ]]; then
    echo "[DRY-RUN] $*"
  else
    "$@"
  fi
}

create_user() {
  local user="$1" group="$2"
  if id "$user" &>/dev/null; then
    echo "[SKIP] $user exists"
    return
  fi
  run useradd -m -s /bin/bash -G "$group" "$user"
  run chage -M 90 -m 1 -W 14 -d 0 "$user"
  echo "[OK] $user provisioned (dry=$DRY_RUN)"
}

# Test run
DRY_RUN=true create_user testuser developers
echo "---"
create_user realuser developers 2>/dev/null || true

Quale comando utilizzare per aggiungere un utente a un gruppo supplementare senza rimuovere le appartenenze esistenti?

In uno script di provisioning in blocco deve assegnare un utente esistente al gruppo auditors. L'utente è già membro di developers e staff. Quale comando conserva tutte le appartenenze esistenti aggiungendo quella nuova?

Riepilogo della lezione: automazione del provisioning di utenti e gruppi

In questa lezione ha creato un toolkit completo per il provisioning degli utenti, adatto alla produzione. Ecco i concetti principali:

  • Analizzare l'input in modo difensivo — utilizzi cicli con IFS/read, salti le righe vuote e di commento e verifichi i campi prima di qualsiasi chiamata di sistema
  • Elementi essenziali di useradd — utilizzi sempre -m (home), -s (shell), -c (commento) e -G (gruppi); verifichi prima l'esistenza con id per garantire l'idempotenza
  • Password — le imposti tramite stdin di chpasswd per evitare che il testo in chiaro compaia negli argomenti del processo; imponga la reimpostazione al primo accesso con chage -d 0
  • chage per i criteri — standardizzi la durata massima (-M), i giorni di avviso (-W) e il blocco per inattività (-I) per tutti gli account non di sistema
  • Appartenenza ai gruppi — utilizzi gpasswd -a o usermod -aG (con il flag -a) per aggiungere invece di sostituire le appartenenze
  • File drop-in di sudoers — scriva in /etc/sudoers.d/, verifichi con visudo -c -f prima dell'installazione e imposti i permessi 0440 root:root
  • Disattivazione — blocchi (usermod -L), archivi la home e poi elimini; non salti mai il passaggio di archiviazione
  • Modalità dry-run — racchiuda i comandi che modificano il sistema in un helper run(), così le pipeline possono verificare la logica senza effetti collaterali che richiedano root

La combinazione di questi modelli fornisce un livello di automazione ripetibile, verificabile e sicuro per la gestione delle identità Linux a qualsiasi scala.

Domande Frequenti

La lezione «Automatizzare la gestione di utenti e gruppi» è gratuita?

Sì — il testo completo di «Automatizzare la gestione di utenti e gruppi» è 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 Linux Command Line & Bash Scripting Mastery, passa a CoddyKit PRO. Il corso Linux Command Line & Bash Scripting Mastery include 4 lezioni in totale.

Cosa imparerò in «Automatizzare la gestione di utenti e gruppi»?

Crei, modifichi e verifichi account in blocco usando useradd, chage e la gestione di frammenti sudoers. Eserciti Linux Command Line & Bash Scripting Mastery 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 Linux Command Line & Bash Scripting Mastery?

Non è richiesta alcuna esperienza precedente. Linux Command Line & Bash Scripting Mastery su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Automatizzare la gestione di utenti e gruppi»?

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 Linux Command Line & Bash Scripting Mastery?

Sì. Ogni lezione Linux Command Line & Bash Scripting Mastery 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. Automatizzare la gestione di utenti e gruppi
  2. Controllare i servizi systemd e scrivere file unit
  3. Automatizzare dischi, filesystem e mount
  4. Creare script per controlli e avvisi sullo stato del sistema
← Torna a Linux Command Line & Bash Scripting Mastery