0Pricing
Linux Command Line & Bash Scripting Mastery · Leçon

Exécuter avec le principe du moindre privilège et discipliner sudo

Abaissez les privilèges, limitez strictement les règles sudo et validez l’UID effectif avant toute opération risquée.

Exécuter avec le principe du moindre privilège et discipliner sudo est une leçon Linux Command Line & Bash Scripting Mastery gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Linux Command Line & Bash Scripting Mastery, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Pourquoi le principe du moindre privilège est important dans les scripts shell

La plupart des failles de sécurité dans l’automatisation ne sont pas dues à des exploits complexes, mais au fait que les scripts s’exécutent avec davantage de privilèges que nécessaire. Une tâche cron exécutée en tant que root qui doit seulement faire tourner un fichier journal est un accident imminent.

Le principe du moindre privilège affirme que chaque processus doit fonctionner uniquement avec les permissions nécessaires à sa tâche — et aucune de plus. Dans les scripts Bash, cela signifie :

  • S’exécuter avec un utilisateur non privilégié chaque fois que possible
  • Élever les privilèges vers root uniquement pour les commandes précises qui l’exigent
  • Abandonner les privilèges dès que le travail privilégié est terminé
  • Ne jamais stocker ni hériter d’identifiants au-delà de leur portée

Cette leçon présente les techniques concrètes : limitation de la portée de sudo, abandon de privilèges avec su, contrôles de validation de l’UID et renforcement de sudoers — afin de construire un modèle discipliné de gestion des privilèges pour les scripts de production.

Vérifier l’UID effectif avant les opérations à risque

Avant tout bloc de code qui nécessite réellement root, votre script doit vérifier qu’il s’exécute avec l’UID effectif attendu. Ne partez jamais du principe que c’est le cas ; vérifiez-le toujours.

$EUID est une variable spéciale de Bash qui contient l’identifiant utilisateur effectif du processus actuel. Root a toujours l’EUID 0. Le vérifier au début d’un script — ou autour d’un bloc privilégié — empêche une exécution accidentelle sous la mauvaise identité.

Utilisez ce modèle de contrôle :

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

# Guard: this script must NOT run as root.
if [[ "$EUID" -eq 0 ]]; then
  echo "ERROR: Do not run this script as root. Use a normal user account." >&2
  exit 1
fi

echo "Running as UID $EUID — proceeding safely."

N’exiger root que lorsque c’est nécessaire

Certains scripts ont légitimement besoin de root. Dans ce cas, le contrôle est inversé : échouez immédiatement si root est absent, plutôt que de laisser le script atteindre un appel système privilégié et produire une erreur de permission déroutante en cours d’exécution.

Associer la sortie anticipée à un message d’utilisation utile rend les scripts explicites :

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

require_root() {
  if [[ "$EUID" -ne 0 ]]; then
    echo "ERROR: $(basename "$0") must be run as root." >&2
    echo "  Try: sudo $(basename "$0") $*" >&2
    exit 1
  fi
}

require_root "$@"

echo "Root confirmed (EUID=0). Starting privileged work..."

Limiter sudo à des commandes uniques

L’erreur la plus courante consiste à placer sudo au début d’un script, puis à tout exécuter en tant que root. Appliquez plutôt sudo uniquement à la commande précise qui en a besoin — tout le reste s’exécute avec votre utilisateur normal.

Cela limite l’étendue des dommages : si un attaquant injecte du code dans votre script, il ne peut exécuter avec les permissions root que les parties précédées de sudo.

Comparez les deux modèles ci-dessous. Le second est nettement plus sûr :

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

# BAD: escalate early, do everything as root (avoid this)
# sudo bash -c '
#   cp config.conf /etc/app/config.conf
#   chown app:app /etc/app/config.conf
#   systemctl restart app
# '

# GOOD: escalate only for the commands that require it
LOCAL_CONF="./config.conf"
DEST="/etc/app/config.conf"

# Unprivileged: validate the config before touching anything as root
if ! grep -q '^[[:space:]]*\[main\]' "$LOCAL_CONF"; then
  echo "ERROR: config.conf is missing [main] section" >&2
  exit 1
fi

# Privileged: only these three commands run under sudo
sudo cp "$LOCAL_CONF" "$DEST"
sudo chown app:app "$DEST"
sudo systemctl restart app

echo "Config deployed and service restarted."

Écrire des règles sudoers strictes

L’appel à sudo somecommand dans un script n’est sûr que si le fichier sudoers est configuré pour autoriser exactement cette commande — et rien de plus. Évitez les règles telles que ALL=(ALL) NOPASSWD: ALL pour les comptes de service.

Limitez plutôt les règles à des commandes précises avec des arguments précis, en utilisant la syntaxe sûre pour visudo. Champs essentiels d’une règle sudoers :

  • Utilisateur — qui peut invoquer sudo
  • Hôte — sur quelle machine (utilisez ALL pour la portabilité)
  • RunAs — quelle identité adopter (presque toujours root)
  • Commande — chemin absolu complet, éventuellement accompagné d’arguments littéraux

Exemples de règles strictes pour un compte de service de déploiement (deployer) :

# /etc/sudoers.d/deployer  (edit with: sudo visudo -f /etc/sudoers.d/deployer)
#
# Allow 'deployer' to restart exactly one service — nothing else
deployer ALL=(root) NOPASSWD: /usr/bin/systemctl restart app

# Allow copying a config file to a fixed destination only
deployer ALL=(root) NOPASSWD: /usr/bin/cp /home/deployer/staging/config.conf /etc/app/config.conf

# Allow chown of that specific file only
deployer ALL=(root) NOPASSWD: /usr/bin/chown app\:app /etc/app/config.conf

# NEVER do this — gives full root shell:
# deployer ALL=(ALL) NOPASSWD: ALL

Abandonner les privilèges avec su et runuser

Lorsqu’un script démarre en tant que root (par exemple, s’il est lancé par une initialisation système ou une tâche cron exécutée en tant que root), mais que la plupart des opérations devraient être effectuées avec un utilisateur non privilégié, abandonnez explicitement les privilèges plutôt que d’exécuter tout le script en tant que root.

Deux outils permettent de le faire :

  • su -s /bin/bash -c 'command' username — lance un shell sous l’identité de username et exécute la commande
  • runuser -u username -- command args — recommandé sous Linux pour changer d’utilisateur dans les scripts appartenant à root ; plus propre que su

Le modèle ci-dessous présente un encapsuleur de déploiement appartenant à root qui passe à l’utilisateur app pour la logique applicative proprement dite :

#!/usr/bin/env bash
# This script is called by systemd as root during pre-deployment
set -euo pipefail

APP_USER="app"
DEPLOY_DIR="/opt/myapp"

# Step 1: privileged — fix ownership of deploy directory
chown -R "${APP_USER}:${APP_USER}" "$DEPLOY_DIR"

# Step 2: drop to app user for the actual migration/startup logic
# runuser is available on most modern Linux systems
runuser -u "$APP_USER" -- bash -c "
  cd $DEPLOY_DIR
  ./bin/migrate.sh
  ./bin/start.sh
"

echo "Deploy complete. Privileged wrapper exiting."

Utiliser sudo -u pour exécuter une commande unique avec un autre utilisateur

Vous n’avez pas toujours besoin d’ouvrir une session shell complète. sudo -u username command exécute une commande unique avec l’utilisateur indiqué, puis revient à l’identité appelante. C’est utile pour modifier des fichiers appartenant à un compte de service sans accorder à ce compte un accès interactif.

Associez cela à une règle sudoers qui autorise exactement cette combinaison utilisateur/commande :

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

# Scenario: deploy script runs as 'deployer'; DB migrations must run as 'postgres'
# sudoers entry needed:
#   deployer ALL=(postgres) NOPASSWD: /opt/app/bin/run_migrations.sh

DB_MIGRATION_SCRIPT="/opt/app/bin/run_migrations.sh"

if [[ ! -x "$DB_MIGRATION_SCRIPT" ]]; then
  echo "ERROR: migration script not found or not executable: $DB_MIGRATION_SCRIPT" >&2
  exit 1
fi

echo "Running DB migrations as postgres user..."
sudo -u postgres "$DB_MIGRATION_SCRIPT"

echo "Migrations done. Returning to deployer context (EUID=$EUID)."

Éviter l’élévation de privilèges via les variables d’environnement

Une surface d’attaque subtile concerne les variables d’environnement héritées par une session sudo. Par défaut, sudo réinitialise l’environnement, mais des configurations incorrectes de env_keep ou des remplacements de env_reset peuvent transmettre aux commandes privilégiées des variables contrôlées par l’attaquant, telles que LD_PRELOAD, PATH ou PYTHONPATH.

Bonnes pratiques :

  • Utilisez toujours des chemins absolus dans les scripts exécutés sous sudo — ne vous reposez jamais sur $PATH
  • Ne transmettez que les variables dont vous avez explicitement besoin : sudo env VAR=value /path/to/cmd
  • Dans sudoers, évitez env_keep += PATH ou env_keep += LD_*
  • Utilisez sudo -E uniquement lorsque vous contrôlez et approuvez entièrement l’environnement appelant
#!/usr/bin/env bash
set -euo pipefail

# BAD: relies on $PATH — attacker who controls PATH can hijack 'cp'
# sudo cp config.conf /etc/app/

# GOOD: absolute paths for every command called under elevated context
SUDO_BIN="/usr/bin/sudo"
CP_BIN="/usr/bin/cp"
CHOWN_BIN="/usr/bin/chown"
SYSTEMCTL_BIN="/usr/bin/systemctl"

"$SUDO_BIN" "$CP_BIN" ./config.conf /etc/app/config.conf
"$SUDO_BIN" "$CHOWN_BIN" app:app /etc/app/config.conf
"$SUDO_BIN" "$SYSTEMCTL_BIN" restart app

echo "Deployed with hardened absolute-path invocations."

Verrouiller sudo avec la validation des arguments de commande

Même lorsqu’une règle sudoers autorise un script précis, ce script peut recevoir des arguments arbitraires si la règle ne les restreint pas également. Voici un piège courant :

deployer ALL=(root) NOPASSWD: /opt/scripts/manage.sh

Cela autorise sudo /opt/scripts/manage.sh restart — mais aussi sudo /opt/scripts/manage.sh --arbitrary-flag. Si manage.sh transmet aveuglément les arguments à des sous-commandes privilégiées, vous avez un problème.

Défendez-vous en profondeur : validez les arguments à l’intérieur du script privilégié ainsi que dans sudoers :

#!/usr/bin/env bash
# /opt/scripts/manage.sh — called via sudo; must validate its own args
set -euo pipefail

# Allowlist of valid actions
declare -A ALLOWED_ACTIONS=(
  [restart]=1
  [status]=1
  [reload]=1
)

ACTION="${1:-}"

if [[ -z "$ACTION" ]]; then
  echo "Usage: $(basename "$0") <restart|status|reload>" >&2
  exit 1
fi

if [[ -z "${ALLOWED_ACTIONS[$ACTION]:-}" ]]; then
  echo "ERROR: Unknown action '${ACTION}'. Allowed: ${!ALLOWED_ACTIONS[*]}" >&2
  exit 2
fi

/usr/bin/systemctl "$ACTION" app
echo "Action '$ACTION' executed successfully."

Élévation temporaire des privilèges avec un piège de nettoyage

Lorsqu’un script doit détenir brièvement un fichier, un identifiant d’authentification ou une ressource nécessitant des privilèges, utilisez trap de Bash afin de garantir le nettoyage, même en cas d’erreur ou de signal. Cela empêche toute fuite de privilèges — par exemple, un binaire setuid temporaire ou un socket appartenant à root qui resterait après l’arrêt brutal du script.

Le modèle ci-dessous crée un fichier temporaire en tant que root, l’utilise, puis le supprime — ce que garantit un piège sur EXIT :

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

# Must run as root for this demo
if [[ "$EUID" -ne 0 ]]; then
  echo "Run as root" >&2; exit 1
fi

TMP_SECRET=""

cleanup() {
  local exit_code=$?
  if [[ -n "$TMP_SECRET" && -f "$TMP_SECRET" ]]; then
    # Overwrite before deletion to reduce forensic recovery risk
    shred -u "$TMP_SECRET" 2>/dev/null || rm -f "$TMP_SECRET"
    echo "[cleanup] Removed privileged temp file." >&2
  fi
  exit "$exit_code"
}

trap cleanup EXIT INT TERM

# Create a root-owned temp file for a short-lived secret
TMP_SECRET="$(mktemp /tmp/deploy_secret.XXXXXXXX)"
chmod 600 "$TMP_SECRET"

# Simulate fetching a secret into the temp file
echo "super-secret-token" > "$TMP_SECRET"

# Use the secret (e.g., pass to a sub-command via file descriptor)
/usr/bin/some-privileged-tool --key-file "$TMP_SECRET"

echo "Privileged operation complete."

Audit et journalisation des actions privilégiées

Le principe du moindre privilège est plus facile à appliquer lorsque chaque événement d’élévation est journalisé avec son contexte : qui a exécuté quoi, quand et pourquoi. Combinez deux niveaux :

  1. sudo lui-même — /var/log/auth.log (Debian/Ubuntu) ou /var/log/secure (RHEL) enregistre automatiquement chaque invocation de sudo
  2. Journal d’audit au niveau du script — écrivez une entrée structurée au début de chaque fonction privilégiée afin que l’intention soit enregistrée parallèlement au journal système

L’utilisation d’une fonction de journalisation structurée simple permet de conserver des pistes d’audit cohérentes et faciles à traiter avec grep :

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

AUDIT_LOG="/var/log/app_deploy_audit.log"

log_privileged_action() {
  local action="$1"
  local reason="${2:-unspecified}"
  local ts
  ts="$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
  printf '{"ts":"%s","user":"%s","euid":%d,"action":"%s","reason":"%s"}\n' \
    "$ts" "${SUDO_USER:-$USER}" "$EUID" "$action" "$reason" \
    | sudo tee -a "$AUDIT_LOG" > /dev/null
}

# Each privileged step is logged before execution
log_privileged_action "cp_config" "deploy release v2.4.1"
sudo /usr/bin/cp ./config.conf /etc/app/config.conf

log_privileged_action "chown_config" "ensure app user owns config"
sudo /usr/bin/chown app:app /etc/app/config.conf

log_privileged_action "restart_service" "activate new config"
sudo /usr/bin/systemctl restart app

echo "Deployment complete. Audit entries written to $AUDIT_LOG"

Vérification des connaissances&nbsp;: portée de sudo

Testez votre compréhension de l’exécution selon le principe du moindre privilège dans les scripts Bash.

Récapitulatif&nbsp;: exécution selon le principe du moindre privilège et utilisation rigoureuse de sudo

Dans cette leçon, vous avez maîtrisé les techniques permettant d’exécuter des scripts Bash avec le minimum de privilèges requis à chaque étape. Voici un résumé concis des principes essentiels :

  • Protégez-vous avec $EUID — échouez immédiatement si le script s’exécute avec la mauvaise identité, qu’il faille refuser root ou l’exiger
  • Limitez la portée de sudo aux commandes individuelles — n’élevez jamais les privilèges de tout un script ; appliquez sudo uniquement aux lignes qui en ont réellement besoin
  • Rédigez des règles sudoers strictes — indiquez les chemins absolus complets et les arguments littéraux ; évitez les caractères génériques et ALL
  • Abaissez les privilèges avec runuser ou sudo -u — lorsqu’un script démarré par root doit passer la main à un utilisateur non privilégié, utilisez l’outil approprié plutôt que d’exécuter tout le script en tant que root
  • Utilisez des chemins absolus — ne vous fiez jamais à $PATH dans du code privilégié ; indiquez explicitement les chemins des binaires pour empêcher leur détournement
  • Validez les arguments dans les scripts privilégiés — les règles sudoers constituent la première ligne de défense, mais pas la seule ; utilisez des listes d’autorisation
  • Interceptez les erreurs et nettoyez — utilisez trap cleanup EXIT pour garantir la destruction des ressources privilégiées temporaires, même en cas d’erreur
  • Journalisez chaque élévation — des entrées d’audit structurées, associées au journal système sudo intégré, assurent la traçabilité de chaque action privilégiée

Appliquées systématiquement, ces pratiques réduisent la surface d’attaque de votre automatisation, qui passe de root-en-permanence à root-uniquement-lorsque-c’est-prouvé-nécessaire — la marque d’un Bash renforcé, prêt pour la production.

Questions Fréquemment Posées

La leçon « Exécuter avec le principe du moindre privilège et discipliner sudo » est-elle gratuite ?

Oui — le texte complet de « Exécuter avec le principe du moindre privilège et discipliner sudo » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Linux Command Line & Bash Scripting Mastery, passe à CoddyKit PRO. Le cours Linux Command Line & Bash Scripting Mastery comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Exécuter avec le principe du moindre privilège et discipliner sudo » ?

Abaissez les privilèges, limitez strictement les règles sudo et validez l’UID effectif avant toute opération risquée. Tu pratiques Linux Command Line & Bash Scripting Mastery avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Linux Command Line & Bash Scripting Mastery ?

Aucune expérience préalable n'est requise. Linux Command Line & Bash Scripting Mastery sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Exécuter avec le principe du moindre privilège et discipliner sudo » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Linux Command Line & Bash Scripting Mastery ?

Oui. Chaque leçon Linux Command Line & Bash Scripting Mastery inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Prévenir l’injection de commandes et d’arguments
  2. Gérer les secrets en toute sécurité et assainir l’environnement
  3. Exécuter avec le principe du moindre privilège et discipliner sudo
  4. Analyse statique et audit avec ShellCheck
← Retour à Linux Command Line & Bash Scripting Mastery