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/zshUtilizzi 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"
doneCreazione 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"
fiImpostazione 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 rando/dev/urandom, visualizzarla una sola volta e obbligare l'utente a modificarla al primo accesso - Impostare una password pre-hashata usando
usermod -pcon 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"
doneGestione 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_FILEcon 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
0440e la proprietà suroot: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 -lpuò 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 è:
- Bloccare immediatamente l'account (
usermod -L): antepone!all'hash della password in shadow, impedendo l'accesso senza eliminare i dati - Revocare sudo: rimuovere il relativo frammento di sudoers, se presente
- Trasferire la proprietà dei suoi file a un responsabile o a un account di archiviazione
- Archiviare la directory home in un tarball prima di eliminarla
- 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 || trueQuale 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 conidper garantire l'idempotenza - Password — le imposti tramite stdin di
chpasswdper evitare che il testo in chiaro compaia negli argomenti del processo; imponga la reimpostazione al primo accesso conchage -d 0 chageper 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 -aousermod -aG(con il flag-a) per aggiungere invece di sostituire le appartenenze - File drop-in di sudoers — scriva in
/etc/sudoers.d/, verifichi convisudo -c -fprima dell'installazione e imposti i permessi0440 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
- Automatizzare la gestione di utenti e gruppi
- Controllare i servizi systemd e scrivere file unit
- Automatizzare dischi, filesystem e mount
- Creare script per controlli e avvisi sullo stato del sistema