לדלג לתוכן

בידוד filesystem ורשת של תהליך

המפה אינה החדר

נניח ששני תהליכים פותחים את הנתיב /data/config ומקבלים קבצים שונים. הסיבה יכולה להיות נקודות עיגון שונות, גם כאשר שתי התוכניות רצות באותו מחשב ומשתמשות באותו קרנל. מרחב שמות של עיגונים מחזיק את המפה שמחברת מערכות קבצים לתוך עץ הנתיבים. יצירת המפה אינה מעתיקה את תוכן הדיסק. שיתוף אותו אובייקט אחסון דרך שתי מפות עדיין מאפשר לראות שינויים בתוכן, בהתאם להרשאות ולמצב העיגון.

כדי לבודד שירות עלינו לענות על כמה שאלות נפרדות. ראשית נחליט אילו קבצים נראים לו. שנית נחליט אילו מהם ניתנים לשינוי. שלישית נבדוק אילו הפניות כבר הגיעו אליו לפני שהפעלנו את ההגבלות. לבסוף נבדוק האם הוא יכול לשנות בעצמו את המפה ולהחזיר לעצמו גישה. רשימה של נתיבים מותרים מספיקה רק כאשר כל ארבע השאלות מטופלות.

שינוי מפה בתוך תהליך מעבדה

נריץ את הניסוי הבא במכונה הווירטואלית של הקורס, עם כלי unshare, כלי mount וכלי findmnt. נשתמש בהרשאות מנהל כדי שהניסוי לא יהיה תלוי במדיניות מרחבי המשתמש של ההפצה. הקובץ הבא הוא סקריפט מלא בשם mount-lab.sh, והפעלתו היא sudo bash mount-lab.sh. הבידוד נוצר לפני פעולות העיגון, ואנו הופכים את העץ לפרטי לפני הוספת עיגונים.

#!/usr/bin/env bash
set -euo pipefail
base=$(mktemp -d /tmp/amit-mount.XXXXXX)
mkdir "$base/source" "$base/view"
printf 'before\n' > "$base/source/value"
cleanup() {
    rm -f -- "$base/source/value"
    rmdir -- "$base/source" "$base/view" "$base"
}
trap cleanup EXIT
unshare --mount --propagation private bash -euo pipefail -c '
    base=$1
    mount --bind "$base/source" "$base/view"
    mount -o remount,bind,ro "$base/view"
    findmnt -T "$base/view" -o TARGET,OPTIONS,PROPAGATION
    cat "$base/view/value"
    if printf "forbidden\n" > "$base/view/value"; then
        echo "unexpected writable view" >&2
        exit 1
    fi
    printf "after\n" > "$base/source/value"
    cat "$base/view/value"
    umount "$base/view"
' bash "$base"
test ! -e "$base/view/value"
cat "$base/source/value"

הפלט הצפוי מכיל תחילה את המילה before, שגיאת כתיבה דרך התצוגה, ובהמשך את המילה after. הכתיבה דרך המקור משנה את אותו קובץ שהתצוגה קוראת. הדגל לקריאה בלבד מגן על מסלול הגישה דרך העיגון, ואינו הופך את תוכן הקובץ לבלתי משתנה בכל המערכת. תוצאה כזאת חשובה במיוחד כששירות מקבל ספריית תצורה לקריאה בלבד, אבל שירות אחר ממשיך לעדכן אותה.

הפקודה remount,bind,ro כאן מטפלת בעיגון היחיד שיצרנו. עץ המכיל עיגונים פנימיים דורש בדיקה נפרדת של אותם עיגונים. הניסוי אינו טוען שהפך באופן רקורסיבי כל עיגון אפשרי לקריאה בלבד. בדקו את הפלט של findmnt ואת הנתיבים הממשיים שהשירות יכול להגיע אליהם, במקום להסיק מהאפשרות המופיעה בשורת הפקודה שכל העץ מוגן.

התפשטות עיגונים והפניות חיות

העתקת רשימת העיגונים יכולה להשאיר יחסי התפשטות בין העותקים. עיגון מסוג shared משתתף בקבוצה שבה אירועים מסוימים עוברים בין חברים. עיגון מסוג slave יכול לקבל אירועים מהמקור שלו בלי להפיץ אליו אירועים בכיוון ההפוך. עיגון מסוג private אינו משתתף בהפצה כזאת. לכן עצם יצירת מרחב עיגונים חדש אינה תשובה מספקת לשאלה אם עיגון שנוסיף ייראה גם במקום אחר.

הפקודה mount --make-rprivate / היא דרך מקובלת להפסיק התפשטות באופן רקורסיבי בתוך מרחב שכבר נוצר. הסקריפט השתמש באפשרות המקבילה של unshare. אל תריצו את הפעולה במרחב העיגונים של המארח מתוך הרגל. משמעות הפעולה נקבעת על ידי המרחב שבו התהליך נמצא ברגע הקריאה, ולא על ידי שם הקובץ שבו כתבנו אותה.

ניתוק עיגון גם אינו שקול לסגירת כל הקבצים מתוכו. קובץ פתוח מחזיק הפניה לאובייקטים שהקרנל צריך כדי לבצע פעולות עליו. ניתוק עצל עם MNT_DETACH מסיר את העיגון מעץ החיפוש, בעוד הפניות קיימות יכולות להמשיך להחזיק משאבים. לפיכך הצלחת ניתוק אינה הוכחה שהעבודה הסתיימה או שהאחסון פנוי לשימוש אחר.

החלפת שורש בלי להשאיר דלת צדדית

הקריאה chroot משנה את השורש המשמש לפתרון נתיבים, אבל אינה בונה סביב התהליך מדיניות אבטחה מלאה. תיקיית העבודה הנוכחית, קבצים פתוחים, הרשאות ותהליכים קשורים עדיין דורשים טיפול. תהליך בעל סמכויות מתאימות יכול לנצל סביבת עבודה שהוכנה בצורה לקויה. לכן נעדיף לדבר על החלפת תצוגת קבצים כחלק ממכלול, ונבדוק כל ערוץ גישה שנותר.

הקריאה pivot_root משנה את עיגון השורש בתוך מרחב העיגונים. נבנה שורש חדש שהוא בעצמו נקודת עיגון, ניצור בתוכו מקום לשורש הישן, נבצע את ההחלפה, נעבור לתיקיית העבודה החדשה ונסיר את התצוגה הישנה. הרצף הבא הוא חלק הכנה למערכת שורש שבנינו מראש, ואינו סקריפט עצמאי להורדת הפצה או להפעלת שירות.

unshare mount namespace
make mount propagation recursively private
bind-mount new_root onto itself
create new_root/old_root
pivot_root(new_root, new_root/old_root)
chdir("/")
close inherited descriptors outside the intended interface
unmount /old_root
remove /old_root
drop setup capabilities
exec service

כל שלב חייב להיכשל בצורה סגורה. כישלון בודד אינו סיבה להמשיך אל exec ולהכריז שהשירות מבודד. יש לתכנן גם את קובצי ההתקנים, מערכת /proc, ספריות דינמיות, קובצי אמון ותיקיות כתיבה. הוספת כל מערכת הקבצים של המארח כדי לפתור שגיאת ספרייה יכולה לבטל את מטרת הבידוד.

חיבור רשת הוא החלטת מדיניות

מרחב רשת חדש מכיל מצב נפרד של ממשקים, ניתוב וסוקטים רלוונטיים. יצירתו אינה מחברת אותו אוטומטית לאינטרנט. זוג ממשקי veth מאפשר להעביר מנות בין שני קצוות, וכל קצה יכול לחיות במרחב אחר. לאחר מכן עדיין דרושים כתובות, מצב פעיל ונתיב מתאים. הפניה אל שרת שמות אינה יוצרת נתיב אל אותו שרת.

ניצור שני מרחבי מעבדה המחוברים זה לזה בלבד. לפני ההרצה ודאו ששמות המעבדה אינם קיימים. הפקודות דורשות הרשאות מנהל ומיועדות למכונה הווירטואלית.

sudo ip netns add amit-isolate-a
sudo ip netns add amit-isolate-b
sudo ip link add amit-va type veth peer name amit-vb
sudo ip link set amit-va netns amit-isolate-a
sudo ip link set amit-vb netns amit-isolate-b
sudo ip -n amit-isolate-a addr add 192.0.2.1/30 dev amit-va
sudo ip -n amit-isolate-b addr add 192.0.2.2/30 dev amit-vb
sudo ip -n amit-isolate-a link set lo up
sudo ip -n amit-isolate-b link set lo up
sudo ip -n amit-isolate-a link set amit-va up
sudo ip -n amit-isolate-b link set amit-vb up
sudo ip netns exec amit-isolate-a ping -c 2 192.0.2.2
sudo ip -n amit-isolate-a route get 198.51.100.1

הבדיקה הראשונה אמורה להצליח אם כלי המעבדה והמדיניות מאפשרים הודעות בדיקה. הבדיקה השנייה אמורה לדווח שאין נתיב, משום שלא הגדרנו נתיב ברירת מחדל. נבחין בין היעדר נתיב לבין סינון מנות ובין שניהם לבין שירות שאינו מאזין. בתום הניסוי, לאחר שאין תהליכי מעבדה פעילים, מחקו רק את שני המרחבים שיצרנו עם ip netns del.

בדיקה של הבטחה מסוימת

ננסח הבטחה כגון "השירות יכול לקרוא תצורה, לכתוב רק בתיקיית הנתונים ולתקשר רק עם הצד השני של הקישור". נבדוק קריאה מותרת, כתיבה אסורה, נתיב שלא נחשף, קובץ שהועבר מראש ויעד רשת מחוץ לקישור. כל בדיקה קשורה להבטחה אחרת. הצלחת בדיקת הרשת אינה ראיה לכך שתצוגת הקבצים הוגבלה.

ההסברים על התפשטות נשענים על תיעוד מרחבי העיגון, וחוזה החלפת השורש מופיע בתיעוד הקריאה. המשיכו לקרוא את תנאי השגיאה לפני מימוש מפעיל מכולות, משום שמצב העיגונים בפועל הוא חלק מהקלט לתוכנית.