Intensiv DevOps-uddannelse · Lektion

Kørsel med færrest mulige rettigheder og disciplineret brug af sudo

Nedgradér rettigheder, afgræns sudo-regler stramt, og validér den effektive UID før risikable handlinger.

Lektion 3 af 413 trin

Kørsel med færrest mulige rettigheder og disciplineret brug af sudo er en gratis Intensiv DevOps-uddannelse-lektion på CoddyKit. Dette er lektion 3 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Intensiv DevOps-uddannelse, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Intensiv DevOps-uddannelse-kurset indeholder 4 lektioner i alt.

Hvorfor mindst mulige privilegier er vigtigt i shellscripts

De fleste sikkerhedsbrud i automatisering sker ikke på grund af eksotiske exploits, men fordi scripts kører med flere privilegier, end de har brug for. Et cron-job, der kører som root, men kun skal rotere en logfil, er en ulykke, der venter på at ske.

Princippet om mindst mulige privilegier siger: Hver proces bør kun bruge de tilladelser, der kræves for at udføre sit arbejde — og ikke flere. I Bash-scripting betyder det:

  • At køre som en bruger uden privilegier, når det er muligt
  • Kun at skifte til root for de specifikke kommandoer, der kræver det
  • At afgive privilegier, så snart arbejdet med forhøjede privilegier er udført
  • Aldrig at gemme eller arve legitimationsoplysninger uden for deres omfang

Denne lektion gennemgår de konkrete teknikker: afgrænsning af sudo, privilegieafgivelse med su, vagter til validering af UID og hærdning af sudoers — så du opbygger en disciplineret privileg model for produktionsscripts.

Kontrol af effektivt UID før risikable handlinger

Før enhver kodeblok, der faktisk kræver root, bør dit script bekræfte, at det kører med det forventede effektive UID. Antag det aldrig; kontrollér det altid.

$EUID er en særlig Bash-variabel, der indeholder det effektive bruger-ID for den aktuelle proces. Root har altid EUID 0. Hvis du kontrollerer det øverst i et script — eller omkring en blok med privilegier — forhindrer du utilsigtet kørsel under den forkerte identitet.

Brug dette vagtmønster:

#!/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."

Kontrol af root kun når det er nødvendigt

Nogle scripts har legitimt brug for root. I så fald vender vagten logikken om: afbryd tidligt, hvis root mangler, i stedet for at lade scriptet nå et privilegeret systemkald og give en forvirrende tilladelsesfejl midt under kørslen.

Hvis du kombinerer den tidlige afslutning med en nyttig brugsmeddelelse, bliver scripts selvforklarende:

#!/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..."

Afgrænsning af sudo til enkelte kommandoer

Den mest almindelige fejl er at placere sudo øverst i et script og derefter køre alt som root. Anvend i stedet kun sudo på den nøjagtige kommando, der har brug for det — alt andet kører som din normale bruger.

Det begrænser skadeomfanget: Hvis en angriber indsætter kode i dit script, kan vedkommende kun køre de dele med root-tilladelser, som ikke har sudo foran sig.

Sammenlign de to mønstre nedenfor. Det andet er markant sikrere:

#!/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."

Skriv stramme sudoers-regler

Hvis du kalder sudo somecommand i et script, fungerer det kun sikkert, hvis sudoers-filen er konfigureret til at tillade præcis den kommando — og intet andet. Undgå regler som ALL=(ALL) NOPASSWD: ALL for servicekonti.

Lås i stedet reglerne fast til specifikke kommandoer med specifikke argumenter ved hjælp af den visudo-sikre syntaks. Vigtige felter i en sudoers-regel:

  • Bruger — hvem der må kalde sudo
  • Vært — på hvilken maskine (brug ALL for portabilitet)
  • RunAs — hvilken identitet der skal antages (næsten altid root)
  • Kommando — fuld absolut sti, eventuelt med bogstavelige argumenter

Eksempel på stramme regler for en deployment-servicekonto (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

Afgivelse af privilegier med su og runuser

Når et script starter som root (f.eks. fordi det startes af et systeminitieringssystem eller cron, der kører som root), men det meste af arbejdet bør udføres af en bruger uden privilegier, skal du udtrykkeligt afgive privilegier i stedet for at køre hele scriptet som root.

To værktøjer til dette:

  • su -s /bin/bash -c 'command' username — starter en shell som username og kører kommandoen
  • runuser -u username -- command args — foretrækkes på Linux til skift i scripts, der ejes af root; det er renere end su

Mønsteret nedenfor viser en root-ejet deployment-wrapper, der skifter til brugeren app for den faktiske applikationslogik:

#!/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."

Brug af sudo -u til at køre en enkelt kommando som en anden bruger

Du har ikke altid brug for en hel shellsession. sudo -u username command kører en enkelt kommando som den angivne bruger og vender derefter tilbage til den kaldende identitet. Det er nyttigt til at ændre filer, der ejes af en servicekonto, uden at give kontoen interaktiv adgang.

Kombinér dette med en sudoers-regel, der tillader præcis denne kombination af bruger og kommando:

#!/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)."

Undgå privilegieeskalering via miljøvariabler

En subtil angrebsflade er miljøvariabler, der arves af en sudo-session. Som standard nulstiller sudo miljøet, men forkert konfigurerede tilsidesættelser af env_keep eller env_reset kan sende angriberstyrede variabler som LD_PRELOAD, PATH eller PYTHONPATH ind i privilegerede kommandoer.

Bedste praksis:

  • Brug altid absolutte stier i scripts, der kører under sudo — stol aldrig på $PATH
  • Send kun de variabler, du udtrykkeligt har brug for: sudo env VAR=value /path/to/cmd
  • Undgå i sudoers env_keep += PATH eller env_keep += LD_*
  • Brug kun sudo -E, når du fuldt ud kontrollerer og har tillid til det kaldende miljø
#!/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."

Låsning af sudo med validering af kommandoargumenter

Selv når en sudoers-regel tillader et specifikt script, kan scriptet få vilkårlige argumenter, medmindre reglen også begrænser dem. En almindelig faldgrube:

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

Det tillader sudo /opt/scripts/manage.sh restart — men også sudo /opt/scripts/manage.sh --arbitrary-flag. Hvis manage.sh ukritisk sender argumenter videre til privilegerede underkommandoer, har du et problem.

Forsvar i dybden: Validér argumenter inde i det privilegerede script såvel som i 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."

Midlertidig rettighedsforøgelse med en oprydningsfælde

Når et script kortvarigt skal have en privilegeret fil, legitimationsoplysning eller ressource, skal du bruge B ಏshs trap for at sikre, at oprydningen sker selv ved fejl eller signaler. Det forhindrer lækage af privilegier – for eksempel en midlertidig setuid-binærfil eller en root-ejet socket, der bliver efterladt, hvis scriptet går ned.

Mønsteret nedenfor opretter en midlertidig fil som root, bruger den og fjerner den derefter – garanteret af en trap på 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."

Revision og logning af privilegerede handlinger

Det er lettere at håndhæve princippet om mindst mulige privilegier, når hver rettighedsforøgelse logges med kontekst: hvem kørte hvad, hvornår og hvorfor. Kombiner to lag:

  1. sudo selv – /var/log/auth.log (Debian/Ubuntu) eller /var/log/secure (RHEL) registrerer automatisk alle sudo-kald
  2. Revisionslog på scriptniveau – skriv en struktureret post i starten af hver privilegerede funktion, så hensigten registreres sammen med systemloggen

En enkel funktion til struktureret logning holder revisionsspor ensartede og nemme at søge i med 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"

Videnstjek: Afgrænsning af sudo

Test din forståelse af udførelse med mindst mulige privilegier i Bash-scripts.

Opsummering: Udførelse med mindst mulige privilegier og disciplineret brug af sudo

I denne lektion har du lært teknikkerne til at køre Bash-scripts med de mindst mulige privilegier på hvert trin. Her er en kort opsummering af de vigtigste principper:

  • Kontrollér med $EUID – stop hurtigt, hvis scriptet kører med en forkert identitet, uanset om det betyder, at root skal afvises eller kræves
  • Begræns sudo til individuelle kommandoer – giv aldrig et helt script forhøjede privilegier; brug kun sudo på de linjer, der reelt har brug for det
  • Skriv stramme sudoers-regler – angiv fulde absolutte stier og bogstavelige argumenter; undgå jokertegn og ALL
  • Fjern privilegier med runuser eller sudo -u – når et script, der er startet som root, skal overlade arbejdet til en bruger uden privilegier, skal du bruge det rigtige værktøj i stedet for at køre alt som root
  • Brug absolutte stier – stol aldrig på $PATH i privilegeret kode; indbyg stier til binærfiler for at forhindre kapring
  • Validér argumenter i privilegerede scripts – sudoers-reglerne er den første forsvarslinje, ikke den eneste; brug tilladelseslister
  • Brug trap og ryd op – brug trap cleanup EXIT for at garantere, at midlertidige privilegerede ressourcer destrueres, selv ved fejl
  • Log hver rettighedsforøgelse – strukturerede revisionsposter kombineret med den indbyggede sudo-syslog giver sporbarhed for hver privilegerede handling

Anvendt konsekvent reducerer denne praksis angrebsfladen for din automatisering fra root-hele-tiden til root-kun-hvor-det-beviseligt-er-nødvendigt – kendetegnet for hærdet Bash i produktionskvalitet.

Gratis at komme i gang

Lær Intensiv DevOps-uddannelse med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
142
Lektioner
568

Ofte stillede spørgsmål

Er lektionen “Kørsel med færrest mulige rettigheder og disciplineret brug af sudo” gratis?

Ja — alle 3 lektioner i læringssporet Intensiv DevOps-uddannelse, inklusive “Kørsel med færrest mulige rettigheder og disciplineret brug af sudo”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Intensiv DevOps-uddannelse-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Kørsel med færrest mulige rettigheder og disciplineret brug af sudo”?

Nedgradér rettigheder, afgræns sudo-regler stramt, og validér den effektive UID før risikable handlinger. Du øver dig i Intensiv DevOps-uddannelse med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Intensiv DevOps-uddannelse?

Der kræves ingen tidligere erfaring. Intensiv DevOps-uddannelse på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 3 af 4.

Hvor lang tid tager lektionen “Kørsel med færrest mulige rettigheder og disciplineret brug af sudo”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Intensiv DevOps-uddannelse-lektion?

Ja. Alle Intensiv DevOps-uddannelse-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Forebyggelse af kommando- og argumentinjektion
  2. Sikker håndtering af hemmeligheder og rent miljø
  3. Kørsel med færrest mulige rettigheder og disciplineret brug af sudo
  4. Statisk analyse og audit med ShellCheck
← Tilbage til Intensiv DevOps-uddannelse