0Pricing
DevOps Bootcamp · Pelajaran

Eksekusi dengan Hak Minimum dan Disiplin sudo

Turunkan hak akses, batasi aturan sudo secara ketat, dan validasi UID efektif sebelum operasi berisiko.

Eksekusi dengan Hak Minimum dan Disiplin sudo adalah pelajaran DevOps Bootcamp gratis di CoddyKit. Ini adalah pelajaran 3 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar DevOps Bootcamp, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus DevOps Bootcamp mencakup 4 pelajaran total.

Mengapa Hak Istimewa Minimum Penting dalam Skrip Shell

Sebagian besar pelanggaran keamanan dalam otomasi terjadi bukan karena eksploitasi yang eksotis, melainkan karena skrip berjalan dengan hak istimewa yang lebih besar daripada yang dibutuhkan. Pekerjaan cron yang berjalan sebagai root dan hanya perlu memutar berkas log adalah kecelakaan yang menunggu terjadi.

Prinsip hak istimewa minimum menyatakan: setiap proses harus beroperasi hanya menggunakan hak akses yang diperlukan untuk menjalankan tugasnya — tidak lebih. Dalam pembuatan skrip Bash, artinya:

  • Berjalan sebagai pengguna tanpa hak istimewa jika memungkinkan
  • Meningkatkan hak menjadi root hanya untuk perintah tertentu yang memerlukannya
  • Menurunkan hak segera setelah pekerjaan dengan hak istimewa selesai
  • Jangan pernah menyimpan atau mewarisi kredensial di luar cakupannya

Pelajaran ini membahas teknik konkret: pembatasan cakupan sudo, penurunan hak dengan su, pemeriksaan UID, dan penguatan sudoers — untuk membangun model hak istimewa yang disiplin bagi skrip produksi.

Memeriksa UID Efektif Sebelum Operasi Berisiko

Sebelum blok kode apa pun yang benar-benar memerlukan root, skrip Anda harus memverifikasi bahwa skrip berjalan dengan UID efektif yang diharapkan. Jangan berasumsi; selalu lakukan penegasan.

$EUID adalah variabel khusus Bash yang menyimpan ID pengguna efektif dari proses saat ini. Root selalu memiliki EUID 0. Memeriksanya di bagian awal skrip — atau di sekitar blok dengan hak istimewa — mencegah eksekusi yang tidak sengaja dengan identitas yang salah.

Gunakan pola pemeriksaan ini:

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

Menegaskan Root Hanya Saat Diperlukan

Beberapa skrip memang memerlukan root. Dalam kasus tersebut, pemeriksaannya dibalik: gagal lebih awal jika root tidak tersedia, alih-alih membiarkan skrip mencapai syscall dengan hak istimewa lalu menghasilkan kesalahan hak akses yang membingungkan di tengah eksekusi.

Menggabungkan keluar lebih awal dengan pesan penggunaan yang informatif membuat skrip dapat menjelaskan penggunaannya sendiri:

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

Membatasi sudo ke Perintah Tunggal

Kesalahan yang paling umum adalah menempatkan sudo di bagian awal skrip lalu menjalankan semuanya sebagai root. Sebagai gantinya, terapkan sudo hanya pada perintah persis yang membutuhkannya — semua hal lainnya berjalan sebagai pengguna biasa Anda.

Ini membatasi dampak serangan: jika penyerang menyisipkan kode ke dalam skrip, mereka hanya dapat menjalankan bagian yang tidak memiliki sudo di depannya dengan hak root.

Bandingkan dua pola di bawah ini. Pola kedua jauh lebih aman:

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

Menulis Aturan sudoers yang Ketat

Memanggil sudo somecommand dalam skrip hanya aman jika berkas sudoers dikonfigurasi untuk mengizinkan tepat perintah tersebut — dan tidak ada yang lain. Hindari aturan seperti ALL=(ALL) NOPASSWD: ALL untuk akun layanan.

Sebagai gantinya, batasi aturan pada perintah tertentu dengan argumen tertentu menggunakan sintaks yang aman untuk visudo. Kolom utama dalam aturan sudoers:

  • Pengguna — siapa yang boleh menjalankan sudo
  • Host — di mesin mana (gunakan ALL agar portabel)
  • RunAs — identitas yang akan digunakan (hampir selalu root)
  • Perintah — jalur absolut lengkap, dengan argumen literal jika diperlukan

Contoh aturan ketat untuk akun layanan deployment (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

Menurunkan Hak Istimewa dengan su dan runuser

Ketika skrip dimulai sebagai root (misalnya, diluncurkan oleh init sistem atau cron yang berjalan sebagai root), tetapi sebagian besar pekerjaan seharusnya dilakukan oleh pengguna tanpa hak istimewa, turunkan hak secara eksplisit alih-alih menjalankan seluruh skrip sebagai root.

Dua alat untuk tujuan ini:

  • su -s /bin/bash -c 'command' username — menjalankan shell sebagai username dan menjalankan perintah
  • runuser -u username -- command args — lebih disarankan di Linux untuk berpindah pengguna dalam skrip yang dimiliki root; lebih bersih daripada su

Pola di bawah ini menunjukkan pembungkus deployment milik root yang menurunkan hak ke pengguna app untuk logika aplikasi yang sebenarnya:

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

Menggunakan sudo -u untuk Menjalankan Satu Perintah sebagai Pengguna Lain

Anda tidak selalu perlu beralih ke sesi shell penuh. sudo -u username command menjalankan satu perintah sebagai pengguna yang ditentukan, kemudian kembali ke identitas pemanggil. Ini berguna untuk memodifikasi berkas yang dimiliki akun layanan tanpa memberikan akses interaktif apa pun kepada akun tersebut.

Padukan ini dengan aturan sudoers yang hanya mengizinkan kombinasi pengguna/perintah tersebut:

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

Mencegah Eskalasi Hak Istimewa melalui Variabel Lingkungan

Salah satu permukaan serangan yang kerap luput diperhatikan adalah variabel lingkungan yang diwarisi oleh sesi sudo. Secara bawaan, sudo mengatur ulang lingkungan, tetapi penggantian env_keep atau env_reset yang salah konfigurasi dapat meneruskan variabel yang dikendalikan penyerang seperti LD_PRELOAD, PATH, atau PYTHONPATH ke perintah dengan hak istimewa.

Praktik terbaik:

  • Selalu gunakan jalur absolut dalam skrip yang berjalan di bawah sudo — jangan pernah bergantung pada $PATH
  • Teruskan hanya variabel yang secara eksplisit diperlukan: sudo env VAR=value /path/to/cmd
  • Dalam sudoers, hindari env_keep += PATH atau env_keep += LD_*
  • Gunakan sudo -E hanya jika Anda sepenuhnya mengendalikan dan memercayai lingkungan pemanggil
#!/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."

Mengunci sudo dengan Validasi Argumen Perintah

Meskipun aturan sudoers mengizinkan skrip tertentu, skrip tersebut masih dapat menerima argumen sembarang kecuali aturan itu juga membatasi argumennya. Jebakan yang umum:

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

Ini mengizinkan sudo /opt/scripts/manage.sh restart — tetapi juga sudo /opt/scripts/manage.sh --arbitrary-flag. Jika manage.sh meneruskan argumen secara membabi buta ke subperintah dengan hak istimewa, Anda menghadapi masalah.

Gunakan pertahanan berlapis: validasi argumen di dalam skrip dengan hak istimewa sekaligus di 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."

Peningkatan Hak Istimewa Sementara dengan Trap Pembersihan

Ketika skrip harus memegang berkas, kredensial, atau sumber daya dengan hak istimewa untuk sementara, gunakan trap milik Bash untuk memastikan pembersihan tetap dilakukan meskipun terjadi kesalahan atau sinyal. Hal ini mencegah kebocoran hak istimewa—misalnya biner setuid sementara atau soket milik root yang tertinggal jika skrip mengalami kerusakan.

Pola di bawah ini membuat berkas sementara sebagai root, menggunakannya, lalu menghapusnya—dijamin oleh trap pada 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."

Mengaudit dan Mencatat Tindakan dengan Hak Istimewa

Hak istimewa minimum lebih mudah diterapkan jika setiap peristiwa peningkatan dicatat beserta konteksnya: siapa yang menjalankan apa, kapan, dan mengapa. Gabungkan dua lapisan:

  1. sudo itu sendiri—/var/log/auth.log (Debian/Ubuntu) atau /var/log/secure (RHEL) secara otomatis mencatat setiap pemanggilan sudo
  2. log audit tingkat skrip—tulis entri terstruktur di awal setiap fungsi dengan hak istimewa agar maksudnya tercatat bersama log sistem

Menggunakan fungsi log terstruktur yang sederhana membuat jejak audit tetap konsisten dan mudah digunakan dengan 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"

Uji Pengetahuan: Cakupan sudo

Uji pemahaman Anda tentang eksekusi dengan hak istimewa minimum dalam skrip Bash.

Rangkuman: Eksekusi dengan Hak Istimewa Minimum dan Disiplin sudo

Dalam pelajaran ini, Anda menguasai teknik untuk menjalankan skrip Bash dengan hak istimewa minimum yang diperlukan pada setiap langkah. Berikut ringkasan singkat prinsip-prinsip utamanya:

  • Lindungi dengan $EUID—gagal secepatnya jika skrip berjalan dengan identitas yang salah, baik berarti menolak root maupun mewajibkannya
  • Batasi cakupan sudo ke perintah individual—jangan pernah meningkatkan hak istimewa seluruh skrip; terapkan sudo hanya pada baris yang benar-benar memerlukannya
  • Tulis aturan sudoers yang ketat—tentukan jalur absolut lengkap dan argumen literal; hindari karakter pengganti dan ALL
  • Turunkan hak istimewa dengan runuser atau sudo -u—ketika skrip yang dimulai sebagai root harus menyerahkan kendali kepada pengguna tanpa hak istimewa, gunakan alat yang tepat, bukan menjalankan semuanya sebagai root
  • Gunakan jalur absolut—jangan pernah mengandalkan $PATH di dalam kode dengan hak istimewa; tetapkan jalur biner secara langsung untuk mencegah pembajakan
  • Validasi argumen di dalam skrip dengan hak istimewa—aturan sudoers adalah garis pertahanan pertama, bukan satu-satunya; gunakan daftar yang diizinkan
  • Gunakan trap dan lakukan pembersihan—gunakan trap cleanup EXIT untuk menjamin sumber daya sementara dengan hak istimewa dihancurkan meskipun terjadi kesalahan
  • Catat setiap peningkatan—entri audit terstruktur yang dipadukan dengan syslog sudo bawaan memberi Anda keterlacakan untuk setiap tindakan dengan hak istimewa

Jika diterapkan secara konsisten, praktik-praktik ini mengurangi permukaan serangan otomatisasi Anda dari root-setiap-saat menjadi root-hanya-saat-terbukti-diperlukan—ciri khas Bash yang diperkuat untuk produksi.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Eksekusi dengan Hak Minimum dan Disiplin sudo” gratis?

Ya — teks lengkap “Eksekusi dengan Hak Minimum dan Disiplin sudo” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus DevOps Bootcamp, upgrade ke CoddyKit PRO. Kursus DevOps Bootcamp mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Eksekusi dengan Hak Minimum dan Disiplin sudo”?

Turunkan hak akses, batasi aturan sudo secara ketat, dan validasi UID efektif sebelum operasi berisiko. Kamu berlatih DevOps Bootcamp dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai DevOps Bootcamp?

Tidak diperlukan pengalaman sebelumnya. DevOps Bootcamp di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 3 dari 4.

Berapa lama pelajaran “Eksekusi dengan Hak Minimum dan Disiplin sudo” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran DevOps Bootcamp ini?

Ya. Setiap pelajaran DevOps Bootcamp menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Mencegah Injeksi Perintah dan Argumen
  2. Penanganan Rahasia yang Aman dan Kebersihan Environment
  3. Eksekusi dengan Hak Minimum dan Disiplin sudo
  4. Analisis Statis dan Audit dengan ShellCheck
← Kembali ke DevOps Bootcamp