chroot#
משנה את ספריית השורש של התהליך הנוכחי וכל ילד שהוא ייצור בהמשך.
לאחר chroot מוצלח, כל שם נתיב המתחיל ב־/ נפתר ביחס לספרייה הנקובה ב־FILENAME במקום שורש מערכת הקבצים האמיתי. התהליך מאבד את היכולת לנקוב בשמות של קבצים מחוץ לתת־עץ זה באמצעות נתיבים מוחלטים. הקרנל אוכף זאת עבור התהליך הנוכחי ועבור כל צאצא - אין תחום פר־thread ואין דרך לבטל זאת מתוך הכלא.
chroot אינו משנה את ספריית העבודה הנוכחית. אם ספריית העבודה הייתה מחוץ לשורש החדש כשהקריאה הצליחה, היא נשארת בחוץ, ונתיבים יחסיים עדיין יכולים לברוח. תמיד עקוב אחרי chroot מוצלח ב־chdir ל־/ (שמשמעו כעת בתוך הכלא) לפני שתעשה כל דבר אחר.
הקריאה מוגבלת ל־super-user. תהליך לא־מורשה מקבל EPERM ו־$! נקבע בהתאם.
תקציר#
chroot FILENAME
chroot
מה מקבלים בחזרה#
1 בהצלחה, 0 בכישלון. בכישלון $! מחזיק את ה־errno מקריאת המערכת chroot(2) שמתחת - בדרך כלל EPERM (לא root), ENOENT (הנתיב אינו קיים), או ENOTDIR (הנתיב אינו ספרייה).
תמיד בדוק את ערך ההחזרה. כישלון שקט משאיר את התהליך פועל מחוץ לכלא המיועד, מה שבדרך כלל גרוע יותר ממסלול הקוד שביצע את הקריאה מלכתחילה.
chroot "/var/empty"
or die "chroot: $!";
chdir "/"
or die "chdir after chroot: $!";
מצב גלובלי שהוא נוגע בו#
דוגמאות#
הרצף הבטוח הקנוני - שנה את השורש, אז שנה את ספריית העבודה לשורש החדש, אז ויתר על הרשאות:
chroot "/srv/jail" or die "chroot: $!";
chdir "/" or die "chdir: $!";
$) = $(= 65534; # drop to 'nobody' gids
$> = $< = 65534; # drop to 'nobody' uids
פתח קבצים וטען ספריות משותפות לפני chroot, לא אחרי. בשורש החדש בדרך כלל אין לא /etc/resolv.conf ולא את נתיבי החיפוש של ה־linker הדינמי:
open my $log, ">>", "/var/log/app.log" or die $!;
my $cfg = read_config("/etc/app.conf");
chroot "/srv/jail" or die "chroot: $!";
chdir "/" or die "chdir: $!";
# $log and $cfg are still usable inside the jail.
בדיקת מצבי כישלון בנפרד - הבחנה בין ״לא root״ ל־״נתיב גרוע״ חשובה לאבחון:
use Errno qw(EPERM ENOENT ENOTDIR);
unless (chroot $path) {
die "chroot: need root privileges\n" if $! == EPERM;
die "chroot: $path does not exist\n" if $! == ENOENT;
die "chroot: $path is not a directory\n" if $! == ENOTDIR;
die "chroot: $!";
}
צורת ארגומנט ברירת המחדל קוראת את הנתיב מ־$_:
$_ = "/srv/jail";
chroot or die "chroot: $!";
מקרי קצה#
לא root: נכשל עם
EPERM. אין הקלה מבוססת־יכולות בלינוקס סטנדרטי אלא אם התהליך מחזיקCAP_SYS_CHROOT.נתיב יחסי:
chroot "jail"נפתר כנגד ספריית העבודה הנוכחית ברגע הקריאה. העדף נתיבים מוחלטים כך שההתנהגות אינה תלויה ב־cwdשל הקורא.symlinks ב־
FILENAME: נפתרים לפני שה־chroot נכנס לתוקף. symlinks בתוך השורש החדש המצביעים על נתיבים מוחלטים ייפתרו ביחס לשורש החדש, לא לשורש האמיתי - זוהי התנהגות הכליאה המיועדת, לא באג.file descriptors פתוחים שורדים: כל מטפל שנפתח לפני הקריאה נשאר שמיש לאחר מכן. כך daemons מתנהגים היטב שומרים על לוגים, קונפיגורציה, וסוקטים מאזינים על פני המעבר.
אין בריחה מבפנים: תהליך אינו יכול
chrootאת דרכו החוצה. הדרך היחידה לעזוב chroot היא שתהליך מורשה מחוץ לו יאסוף ויחליף אותו.ספריית עבודה מחוץ לשורש החדש: הקרנל מתיר את הקריאה, אך נתיבים יחסיים ומעבר
..משם עדיין יכולים להגיע לקבצים מחוץ לכלא. ה־chdir "/"החובה לאחרchrootסוגר את הפער.Threads:
chrootבלינוקס הוא פר־process (במדויק, פר fs struct). תחתuse threadsכל ה־threads של המפענח חולקים את אותו שורש לאחר הקריאה - אין כלא פר־thread.אינו משפיע על
$0,%ENV, או סוקטים פתוחים. רק פתרון נתיב משתנה.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
chdir- המעקב החובה לאחרchrootמוצלח; בלעדיו ספריית העבודה עשויה עדיין להצביע מחוץ לשורש החדשfork- ילדים יורשים את ה־chroot; שלב עםchrootכדי להריץ קוד לא־מהימן בתת־עץ מוגבלexec- מריץ את תוכנית ההחלפה בתוך השורש הנוכחי, כך שהקובץ הבינארי המטרה והספריות הדינמיות שלו חייבים להתקיים בתוך הכלאopen- פתיחת קבצים בנתיב מוחלט לאחרchrootמחפשת שמות בתוך הכלא; פתח מראש כל מה שאתה צריך מבחוץ לפני הקריאה$_- נתיב ברירת המחדל כאשרchrootנקרא ללא ארגומנט$!- מחזיק את ה־errnoמ־chroot(2)בכישלון