التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo
خفّض الصلاحيات وقيّد قواعد sudo بدقة وتحقّق من UID الفعّال قبل العمليات عالية الخطورة
التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo درس مجاني في DevOps Bootcamp على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في DevOps Bootcamp، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.
أهمية مبدأ أقل قدر من الامتيازات في البرامج النصية للصدفة
تحدث معظم الاختراقات الأمنية في الأتمتة، لا بسبب ثغرات استغلال معقدة، بل لأن البرامج النصية تعمل باستخدام امتيازات تفوق حاجتها. فمهمة cron تعمل كـ root ولا تحتاج إلا إلى تدوير ملف سجل هي حادثة تنتظر الوقوع.
ينص مبدأ أقل قدر من الامتيازات على أن تعمل كل عملية باستخدام الأذونات المطلوبة لإنجاز مهمتها فقط — دون أي أذونات إضافية. وفي كتابة برامج Bash النصية يعني ذلك:
- التشغيل كمستخدم غير مميّز كلما أمكن
- رفع الامتيازات إلى root للأوامر المحددة التي تتطلب ذلك فقط
- التخلي عن الامتيازات فور انتهاء العمل الذي يتطلبها
- عدم تخزين بيانات الاعتماد أو وراثتها خارج نطاقها مطلقًا
يستعرض هذا الدرس التقنيات العملية: تحديد نطاق sudo، والتخلي عن الامتيازات باستخدام su، وحواجز التحقق من UID، وتشديد إعدادات sudoers — لبناء نموذج امتيازات منضبط للبرامج النصية المستخدمة في الإنتاج.
التحقق من UID الفعّال قبل العمليات الخطرة
قبل أي كتلة من التعليمات البرمجية تتطلب root فعلًا، ينبغي أن يتحقق البرنامج النصي من تشغيله باستخدام UID الفعّال المتوقع. لا تفترضوا ذلك؛ بل تحققوا منه دائمًا.
$EUID هو متغير خاص في Bash يحتوي على معرّف المستخدم الفعّال للعملية الحالية. ويكون EUID للمستخدم root دائمًا هو 0. ويساعد التحقق منه في بداية البرنامج النصي — أو حول كتلة مميّزة — على منع التنفيذ غير المقصود بهوية خاطئة.
استخدموا نمط الحماية هذا:
#!/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."التأكد من استخدام root عند الحاجة فقط
تحتاج بعض البرامج النصية إلى root بصورة مشروعة. في هذه الحالة ينعكس دور الحماية: يفشل البرنامج مبكرًا عند غياب root بدلًا من وصوله إلى استدعاء مميّز وإصداره خطأ أذونات مربكًا أثناء التشغيل.
إن الجمع بين الخروج المبكر ورسالة استخدام مفيدة يجعل البرامج النصية موضِّحةً لطريقة استخدامها بذاتها:
#!/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..."تحديد نطاق sudo لأوامر مفردة
الخطأ الأكثر شيوعًا هو وضع sudo في بداية البرنامج النصي ثم تشغيل كل شيء كـ root. بدلًا من ذلك، طبّقوا sudo على الأمر المحدد الذي يحتاج إليه فقط — أما كل ما عداه فيعمل كمستخدمكم العادي.
يحدّ ذلك من نطاق الضرر: فإذا حقن مهاجم تعليمات برمجية في البرنامج النصي، فلن يتمكن من تنفيذ الأجزاء التي لا يسبقها sudo بامتيازات root إلا ضمن أذونات المستخدم العادي.
قارنوا بين النمطين أدناه. فالثاني أكثر أمانًا بكثير:
#!/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."كتابة قواعد sudoers محكمة
لا يكون استدعاء sudo somecommand في برنامج نصي آمنًا إلا إذا تم إعداد ملف sudoers للسماح بذلك الأمر تحديدًا — ولا شيء سواه. تجنبوا قواعد مثل ALL=(ALL) NOPASSWD: ALL لحسابات الخدمة.
بدلًا من ذلك، قيّدوا القواعد بأوامر محددة مع وسائط محددة باستخدام الصياغة الآمنة لـ visudo. الحقول الأساسية في قاعدة sudoers:
- User — من يُسمح له باستدعاء sudo
- Host — على أي جهاز (استخدموا
ALLلتحقيق قابلية النقل) - RunAs — الهوية المطلوب انتحالها (غالبًا ما تكون
root) - Command — المسار المطلق الكامل، مع وسائط حرفية اختياريًا
أمثلة على قواعد محكمة لحساب خدمة النشر (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التخلي عن الامتيازات باستخدام su وrunuser
عندما يبدأ البرنامج النصي كـ root (مثلًا عند تشغيله بواسطة نظام تهيئة أو cron يعمل كـ root)، بينما ينبغي تنفيذ معظم العمل كمستخدم غير مميّز، تخلّوا عن الامتيازات صراحةً بدلًا من تشغيل البرنامج النصي بأكمله كـ root.
أداتان لهذا الغرض:
su -s /bin/bash -c 'command' username— ينشئ صدفة كمستخدم username وينفذ الأمرrunuser -u username -- command args— الخيار المفضّل في Linux للتبديل ضمن البرامج النصية المملوكة لـ root؛ وهو أنظف منsu
يوضح النمط أدناه غلاف نشر مملوكًا لـ root يتخلى عن الامتيازات لصالح المستخدم app لتنفيذ منطق التطبيق الفعلي:
#!/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."استخدام sudo -u لتشغيل أمر واحد كمستخدم آخر
لا تحتاجون دائمًا إلى الانتقال إلى جلسة صدفة كاملة. إذ ينفذ sudo -u username command أمرًا واحدًا كمستخدم محدد، ثم يعيدكم إلى هوية المستخدم المستدعي. ويفيد ذلك في تعديل ملفات مملوكة لحساب خدمة من دون منح ذلك الحساب وصولًا تفاعليًا.
أقرنوا ذلك بقاعدة sudoers تسمح بتركيبة المستخدم/الأمر المحددة تمامًا:
#!/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)."منع تصعيد الامتيازات عبر متغيرات البيئة
هناك سطح هجوم خفي يتمثل في متغيرات البيئة الموروثة من جلسة sudo. فافتراضيًا، يعيد sudo ضبط البيئة، لكن تجاوزات env_keep أو env_reset المُعدّة بصورة خاطئة قد تمرّر متغيرات يتحكم فيها المهاجم، مثل LD_PRELOAD أو PATH أو PYTHONPATH، إلى الأوامر المميّزة.
أفضل الممارسات:
- استخدموا دائمًا مسارات مطلقة في البرامج النصية التي تعمل تحت
sudo— ولا تعتمدوا مطلقًا على$PATH - مرّروا المتغيرات التي تحتاجون إليها صراحةً فقط:
sudo env VAR=value /path/to/cmd - في sudoers، تجنبوا
env_keep += PATHأوenv_keep += LD_* - استخدموا
sudo -Eفقط عندما تكون البيئة المستدعية تحت سيطرتكم وتثقون بها تمامًا
#!/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."تشديد sudo عبر التحقق من وسائط الأوامر
حتى عندما تسمح قاعدة sudoers ببرنامج نصي محدد، يمكن تمرير وسائط عشوائية إليه ما لم تقيّد القاعدة هذه الوسائط أيضًا. ومن الأخطاء الشائعة:
deployer ALL=(root) NOPASSWD: /opt/scripts/manage.shيسمح هذا بتنفيذ sudo /opt/scripts/manage.sh restart — لكنه يسمح أيضًا بتنفيذ sudo /opt/scripts/manage.sh --arbitrary-flag. وإذا مرّر manage.sh الوسائط دون تحقق إلى أوامر فرعية مميّزة، فستنشأ مشكلة.
اعتمدوا الدفاع متعدد الطبقات: تحققوا من الوسائط داخل البرنامج النصي المميّز أيضًا، بالإضافة إلى التحقق منها في 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."رفع الامتيازات مؤقتًا مع مصيدة تنظيف
عندما يجب أن يحتفظ أحد السكربتات مؤقتًا بملف أو بيانات اعتماد أو مورد ذي امتيازات، استخدم trap في Bash لضمان تنفيذ عملية التنظيف حتى عند حدوث خطأ أو وصول إشارة. ويمنع ذلك تسرّب الامتيازات — مثل ترك ملف ثنائي مؤقت setuid أو مقبس مملوك لـ root إذا تعطل السكربت.
ينشئ النمط أدناه ملفًا مؤقتًا بصفة root، ثم يستخدمه ويحذفه — ويضمن ذلك وجود مصيدة على 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."تدقيق الإجراءات ذات الامتيازات وتسجيلها
يصبح فرض مبدأ أقلّ الامتيازات أسهل عندما يُسجَّل كل حدث لرفع الامتيازات مع سياقه: من شغّل ماذا، ومتى، ولماذا. اجمع بين طبقتين:
- sudo نفسه — يلتقط
/var/log/auth.log(في Debian/Ubuntu) أو/var/log/secure(في RHEL) كل استدعاء لـ sudo تلقائيًا - سجل تدقيق على مستوى السكربت — اكتب إدخالًا منظمًا في بداية كل دالة ذات امتيازات، بحيث تُسجَّل النية إلى جانب سجل النظام
يساعد استخدام دالة بسيطة للسجلات المنظمة على إبقاء مسارات التدقيق متسقة وسهلة البحث باستخدام 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"اختبار المعرفة: تحديد نطاق sudo
اختبر مدى فهمك لتنفيذ مبدأ أقلّ الامتيازات في سكربتات Bash.
مراجعة: التنفيذ بأقلّ الامتيازات والانضباط في استخدام sudo
أتقنت في هذا الدرس تقنيات تشغيل سكربتات Bash بالحد الأدنى من الامتيازات المطلوبة في كل خطوة. إليك ملخصًا موجزًا للمبادئ الأساسية:
- تحقّق باستخدام
$EUID— أوقف التنفيذ مبكرًا إذا كان السكربت يعمل بالهوية الخاطئة، سواء كان ذلك يعني رفض root أو اشتراطه - حدّد نطاق
sudoللأوامر الفردية — لا ترفع امتيازات سكربت كامل مطلقًا؛ طبّقsudoعلى الأسطر التي تحتاج إليه فعلًا فقط - اكتب قواعد sudoers محكمة — حدّد المسارات المطلقة الكاملة و الوسائط الحرفية؛ وتجنّب أحرف البدل و
ALL - تخلَّ عن الامتيازات باستخدام
runuserأوsudo -u— عندما يجب على سكربت بدأه root تسليم التنفيذ إلى مستخدم غير ذي امتيازات، استخدم الأداة المناسبة بدلًا من تشغيل كل شيء بصفة root - استخدم المسارات المطلقة — لا تعتمد مطلقًا على
$PATHداخل الشيفرة ذات الامتيازات؛ ثبّت مسارات الملفات الثنائية لمنع الاختطاف - تحقّق من الوسائط داخل السكربتات ذات الامتيازات — قواعد sudoers هي خط الدفاع الأول وليست الوحيد؛ استخدم قوائم السماح
- استخدم المصائد ونفّذ التنظيف — استخدم
trap cleanup EXITلضمان إتلاف الموارد المؤقتة ذات الامتيازات حتى عند حدوث خطأ - سجّل كل عملية رفع للامتيازات — تمنحك إدخالات التدقيق المنظمة، إلى جانب سجل syslog المدمج في sudo، إمكانية تتبّع كل إجراء ذي امتيازات
عند تطبيق هذه الممارسات باستمرار، فإنها تقلّل سطح الهجوم في الأتمتة من root طوال الوقت إلى root فقط حيث تثبت الضرورة — وهو ما يميز Bash المحصّن والمناسب للبيئات الإنتاجية.
الأسئلة الشائعة
هل درس «التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo» مجاني؟
نعم — نص درس «التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة DevOps Bootcamp، انتقل إلى CoddyKit PRO. تتضمن دورة DevOps Bootcamp 4 دروس في المجموع.
ماذا ستتعلم في «التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo»؟
خفّض الصلاحيات وقيّد قواعد sudo بدقة وتحقّق من UID الفعّال قبل العمليات عالية الخطورة تتمرن على DevOps Bootcamp مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ DevOps Bootcamp؟
لا تُشترط خبرة سابقة. DevOps Bootcamp على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس DevOps Bootcamp هذا؟
نعم. كل درس في DevOps Bootcamp يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- منع حقن الأوامر والوسائط
- التعامل الآمن مع الأسرار ونظافة البيئة
- التنفيذ بأقل الصلاحيات والانضباط في استخدام sudo
- التحليل الساكن والتدقيق باستخدام ShellCheck