ההרשאות בלינוקס עונות על השאלה «למי מותר לקרוא את הקובץ הזה». AppArmor עונה על שאלה אחרת: «מה בכלל מותר לתוכנית הזאת לעשות». וההבדל מתגלה בזמן פריצה. web shell רץ תחת המשתמש של שרת האתר ויורש את כל זכויותיו — וזו גישת קריאה לחצי מהמערכת. פרופיל AppArmor מגביל את התהליך לרשימת מה שדרוש לו לעבודתו, וניסיון לקרוא את /etc/shadow או להריץ את /bin/bash מסתיים בסירוב בלי קשר להרשאות המשתמש.
מה כבר מופעל
sudo aa-status
הפלט עונה על שלוש שאלות: כמה פרופילים נטענו, כמה מהם במצב enforce וכמה ב-complain, ואילו תהליכים רצים בלי שום פרופיל. ב-Ubuntu טיפוסי יהיו כמה עשרות פרופילים, אבל כמעט כולם לתוכניות עזר כמו man ו-tcpdump. והשירותים המרכזיים — nginx, Apache, PHP-FPM — נעדרים בדרך כלל מרשימת המוגבלים.
שלושה מצבים שכדאי להבחין ביניהם:
- enforce — הכללים נאכפים, כל מה שמעבר נדחה;
- complain — ההפרות רק נרשמות ביומן, שום דבר אינו נחסם. מצב הכוונון;
- unconfined — אין פרופיל, התוכנית רצה בלי מגבלות.
והחלק המועיל ביותר בפלט של aa-status הוא האחרון: תהליכים שמאזינים לרשת ושום דבר אינו מגביל אותם. ומהרשימה הזאת מתחילים.
הכלים
sudo apt install apparmor-utils
בלי החבילה הזאת הפקודות aa-complain, aa-enforce ו-aa-logprof אינן קיימות במערכת, אף ש-AppArmor עצמו רץ.
איך להפעיל פרופיל בלי לעצור את השירות
הסדר כאן עקרוני. פרופיל שמועבר ישר ל-enforce ידחה בסבירות גבוהה משהו שהשירות זקוק לו, והוא יפסיק לעבוד — בדרך כלל לא מיד אלא בפעולה נדירה כמו העלאת קובץ או שליחת דואר.
הרצף הנכון:
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
שבוע של עבודה במצב הזה בכיסוי כל תרחיש חריג: יצירת גיבוי, עדכון, העלאת קבצים גדולים. אחר כך עברו על מה שהצטבר:
sudo aa-logprof
הפקודה עוברת על ההפרות שנרשמו ושואלת על כל אחת אם להתיר. וכאן דרושה תשומת לב: התירו את מה ששייך באמת לעבודת השירות, ולא כל מה שנקרה לעיניכם — אחרת יתקבל פרופיל שמתיר הכול והמשמעות תאבד.
ורק אז:
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx
איפה רואים את הסירובים
כל ההפרות מגיעות ליומן הליבה:
sudo journalctl -k | grep -i apparmor
sudo grep 'apparmor="DENIED"' /var/log/audit/audit.log
הקובץ השני קיים אם auditd מותקן — ואיתו הרשומות מפורטות במידה ניכרת. רשומת סירוב מציגה את שם הפרופיל, את הפעולה שהתבקשה ואת הנתיב שאליו ניגשו; וזה מספיק כדי להחליט אם לתקן את הפרופיל או לשמוח שהוא פעל.
ותסמין אחד שווה לזכור: שירות מתנהג מוזר — אינו קורא הגדרות, אינו כותב לתיקייה, אינו פותח שקע — בעוד היומנים שלו עצמו מציגים שגיאת «permission denied» עם הרשאות שנראות תקינות לגמרי. זה כמעט תמיד AppArmor, והדבר הראשון לעשות הוא להציץ ביומן הליבה.
אמצעי מעשי, לא תיאורטי
פרופיל ל-PHP-FPM או ל-nginx משתלם באופן קונקרטי. כשהתהליך מוגבל, web shell שהגיע לאתר אינו יכול לא לקרוא קובצי מערכת, לא להריץ מעטפת, ולא לכתוב שום דבר מחוץ לתיקיות המותרות. אתר שנפרץ נשאר אתר שנפרץ במקום להפוך לגישה לכל המכונה — ובדיוק המעבר הזה מהווה את עיקר הנזק.
סדר הטמעה סביר: תחילה פרופיל למה שמביט לאינטרנט (שרת האתר, PHP-FPM), אחר כך למסדי הנתונים, ואחר כך לפי הצורך. אין צורך להפעיל הכול בבת אחת, ואין ערכה מוכנה אוניברסלית: פרופיל תלוי במקום שבו נמצאים הקבצים שלכם.
מה לבדוק באופן סדיר
פרופילים חוזרים ל-unconfined לעיתים קרובות יותר משנדמה: עדכון חבילה עלול להחליף קובץ פרופיל, איתור תקלות ידני משאיר שירות ב-complain, ושירות חדש מגיע בלי שום פרופיל. שני מספרים מועילים: כמה פרופילים ב-enforce, וכמה תהליכים הפונים לרשת נמצאים ב-unconfined. ושווה לדעת על שינוי בכל אחד מהם. איך זה נראה מקובץ בעמוד אחד מראה ההדגמה למטה.