השירות מתחיל, היומן מתלונן שהוא לא יכול לפתוח את התקן ה-tun, והמסלולים אף פעם לא מופיעים. הריצו capsh --print במעטפת שלכם ו-cap_net_admin נמצא בדיוק שם, מה שהופך את הכשל לבלתי אפשרי למראה. מה ששונה בין המעטפת שלכם ליחידה הוא מעבר ההרשאות, ולא הנכונות של הליבה להעניק את היכולת. עברו על ארבע הבדיקות שלמטה לפי הסדר ועצרו בראשונה שמסבירה את הפער.

הפרידו בין יכולת חסרה להתקן מוסתר

שני כשלים נראים זהים משורת היומן. תהליך בלי CAP_NET_ADMIN לא יכול ליצור או להגדיר את ממשק המנהרה, ותהליך שה-sandbox שלו מסתיר את /dev/net/tun לא יכול לפתוח אותו מלכתחילה. הוספת יכולות לא תתקן את המקרה השני.

בדקו אם ההתקן קיים בתוך מרחב השמות של השירות לפני שאתם עורכים משהו. PrivateDevices=yes הוא שורת הקשחה נפוצה שמסירה את צומת ה-tun לגמרי, ו-ProtectSystem מגביל יכול להפוך את נתיב התצורה לקריאה בלבד באופן שנקרא כמו שגיאת הרשאה. שניהם מייצרים שורות יומן שמזכירות את המנהרה, ולכן מאשימים את היכולות. אשרו קודם שהצומת קיים וניתן לפתיחה, ואז המשיכו.

דעו איזו מערכת יכולות אתם רואים

לינוקס מחזיק מערכות מורשות, אפקטיביות, ברות-הורשה, תחומות וסביבתיות, והמערכת הסביבתית היא זו ששורדת exec של משתמש שאינו root. יכולות קובץ על הבינארי יכולות להעביר זכויות מורשות ואפקטיביות ב-exec, אבל רק כל עוד שום דבר בשרשרת לא חוסם את ההעברה הזו.

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

העניקו את היכולת ביחידה, לא בעטיפה

מקטע שירות מינימלי למנהרה שצריכה ליצור התקן ולהתקין מסלולים נראה כך.

[Unit]
Description=OpenVPN client, office profile
After=network-online.target

[Service]
Type=notify
User=openvpn
Group=openvpn
NoNewPrivileges=yes
AmbientCapabilities=CAP_NET_ADMIN
CapabilityBoundingSet=CAP_NET_ADMIN
ExecStart=/usr/sbin/openvpn --config /etc/openvpn/office.conf

AmbientCapabilities מכניס את הזכות למערכת הסביבתית לפני שהתהליך מתחיל, ו-CapabilityBoundingSet מונע מהתהליך להחזיק משהו מחוץ לרשימה הזו. NoNewPrivileges=yes חוסם השגת הרשאות דרך setuid או יכולות קובץ, בעוד היכולת שהיחידה מעניקה במפורש עדיין חלה. הוסיפו CAP_NET_BIND_SERVICE רק כשהשירות חייב לקשור פורט נמוך, ולעולם אל תוסיפו CAP_SYS_ADMIN רק מפני ששרשור בפורום עשה זאת.

אם היחידה עוטפת את הבינארי ב-sudo, הסירו את העטיפה. systemd כבר מחיל User=, והשכבה הנוספת הזו היא הסיבה הסבירה ביותר לכך שהמערכת הסביבתית ריקה.

אמתו מול התהליך החי, לא מול קובץ היחידה

קראו מה המנהל באמת החל, ואז קראו את התהליך שהוא יצר.

unit=openvpn-client@office
systemctl show -p User -p AmbientCapabilities "$unit"
systemctl show -p MainPID --value "$unit" | xargs -r getpcaps
grep -E '^Cap(Eff|Amb)' /proc/$(systemctl show -p MainPID --value "$unit")/status

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

העדיפו לוותר על הרשאות אחרי האתחול

OpenVPN יכול להתחיל עם הרשאות ואז לרדת לחשבון ללא הרשאות דרך אפשרויות ה-user וה-group שלו, בעוד persist-key ו-persist-tun שומרים על המנהרה שמישה לאחר מכן. הסדר כזה מצמצם את החלון שבו התהליך מחזיק זכויות, ולעיתים קרובות הוא מתאים יותר מאשר להפעיל את כל הדמון ללא הרשאות עם יכולות קבועות.

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

מאמרים קשורים