שכבה מועילה, עם גבולות ידועים
הכלי ai-jail נבנה לעצור טעויות של סוכן אמין ולהשאיר את הנזק בתוך הפרויקט: תיקיית הפרויקט היא רדיוס הפגיעה, ותיקיית הבית, המפתחות והרשת שלכם נשארים מחוץ לו. מדובר ב-sandbox ברמת התהליך שחולק איתכם את הקרנל, ולכן הוא לא גבול מול קוד עוין או תוקף נחוש, ולא מחליף מכונה וירטואלית חד-פעמית.
למי שחותם על האישור, ואחר כך למהנדס האבטחה.
מה הוא נבנה לעצור
גם סוכן אמין מקליד פקודה לא נכונה, מריץ סקריפט שמטפס מעל שורש הפרויקט, או מוטעה על ידי דף אינטרנט או חבילה. אלה הדברים שהוא מושיט אליהם יד אחר כך, ולא מוצא.
↔ החליקו לצדדים כדי לראות את התרשים המלא
מחוץ להישג יד
- תיקיית הבית שלכם ופרטי הגישה שבה. הסוכן מקבל תיקיית בית חדשה וריקה, כך ש-
~/.sshו-~/.awsלא קיימים שם. - סודות ב-shell שלכם. הכלי ai-jail מצמצם את משתני הסביבה לרשימת היתרים קטנה, כך שטוקנים שיוצאו נשארים בחוץ. היוצא מן הכלל היחיד הוא מפתח ה-API של ה-harness שהופעל, למשל
ANTHROPIC_API_KEYעבור Claude, שמועתק כברירת מחדל יחד עם ההתחברות שלו;--no-agent-stateעוצר את זה. - הרשת. היא סגורה, עם שני חריגים שעוברים דרך הפרוקסי המסנן: רשימה קצרה של registries של חבילות כשה-toolchains דלוקים, ברירת המחדל ב-Linux, ועבור סוכן מוכר, ה-host של ה-API שלו, בשתי הפלטפורמות. hosts של טלמטריה נשארים סגורים.
--no-networkסוגר את שניהם. - הזיכרון המשותף של המארח, שרת התצוגה ולוח ההעתקה שלכם. הפלט של הסוכן עובר דרך מסנן שזורק sequences של טרמינל שנוגעים בלוח ההעתקה או מתשאלים את הטרמינל.
- הקלדה לתוך הטרמינל שלכם. ב-Linux נחסמת הקריאה
TIOCSTIשמזייפת הקשות, וב-macOS השליטה בטרמינל מוגבלת לטרמינל האחד שנוצר עבור ההרצה. - הכללים שלו עצמו. קובץ
.ai-jailבתוך ריפוזיטורי נחשב לא אמין: הוא יכול להדק את ה-sandbox, ולעולם לא לפתוח אותו.
איפה החומה נגמרת
הכלא והמערכת שלכם עומדים על אותו קרנל. באג בקרנל הזה, בדרייבר או ב-backend של ה-sandbox הוא דרך לעקוף את החומה, ו-ai-jail לא יכול לעשות שום דבר בעניין. מכונה וירטואלית מביאה קרנל משלה, ולכן היא גבול חזק יותר.
↔ החליקו לצדדים כדי לראות את התרשים המלא
מה זה אומר על הנתונים שלכם
- סודות בתוך הפרויקט. קובץ
.envבריפוזיטורי קריא, אלא אם מיסכתם אותו. כך ממסכים קבצים. - מיסוך מכסה רק נתיבים שקיימים כשה-sandbox עולה. קובץ סודות שנוצר מאוחר יותר, במהלך הסשן, לא מוסתר.
- host מורשה זוכה לאמון מלא. מי שמתיר את
github.comעם--allow-hostנותן לסוכן לעשות push לכל ריפוזיטורי שם, לא רק לשלכם. --networkפותח את כל הרשת, ולכן כל מה שהסוכן יכול לקרוא, כולל הפרויקט שלכם, הוא יכול לשלוח החוצה.- הכלי ai-jail מעגן את ההתחברות של הסוכן עצמו כברירת מחדל, לקריאה וכתיבה, ולכן סוכן שהוטעה יכול להשתמש בה למשך ההרצה. הפרויקט מתעד את זה כפשרה לטובת הנוחות;
--no-agent-stateנותן הרצה ללא התחברות. - git בצד המארח סומך על
.git/של הפרויקט, שפתוח לכתיבה. תוכנית ששמה מופיע ב-.git/config, למשל ב-core.fsmonitorאו ב-core.pager, או שהונחה ב-.git/hooks/, רצה בשמכם, מחוץ ל-sandbox, בפעם הבאה שפקודת git מתאימה רצה על המארח; ל-core.fsmonitorמספיקgit statusרגיל, גם כזה שהעורך שלכם מריץ ברקע, ושום דבר לא מופיע ב-git diff.--lockdownסוגר את זה בכך שהפרויקט הופך לקריאה בלבד, ו---map .gitשומר את הריפוזיטורי לקריאה בלבד בהרצה רגילה, במחיר של commit מבפנים. אחרי סשן שהפתיע אתכם, קראו את.git/configורשמו את התוכן של.git/hooksעם כלי קבצים רגילים לפני שאתם מריצים git על המארח, כיgit config --listבעצמו מפעיל את ה-pager שהוגדר. --maskו---deny-pathמשנים רק את מה שה-sandbox רואה. כל מה שמחוץ ל-sandbox ופועל בשם הסוכן עדיין קורא את הקבצים האמיתיים: ה-daemon של Docker תחת--docker, שרת MCP שמגיעים אליו דרך socket ממופה, או שירות שמאחורי--forward-port.
למהנדס האבטחה
- פגיעויות בקרנל ובדרייברים. כל תהליך שרץ ב-sandbox מדבר עם אותו קרנל כמו שאר המחשב שלכם.
- באגים באמולטור הטרמינל שלכם, בעיקר עם
--terminal-passthrough, שמכבה את מסנן הפלט. - פגמים ב-backend של ה-sandbox עצמו: bubblewrap, Landlock ו-seccomp ב-Linux, או
sandbox-execב-macOS. - Landlock, הכללים של הקרנל עצמו, מגדר sockets אבסטרקטיים של Unix וסיגנלים רק בקרנלים 6.12 ומעלה, במאמץ מיטבי. קרנלים ישנים יותר נשארים רק עם כללי מערכת הקבצים והרשת, ולכן עם
--x11,--networkאו--systemd-userה-sockets האבסטרקטיים והסיגנלים של המארח נשארים נגישים. - ערוצי צד (side channels) וכמה סוגים של תקשורת בין-תהליכית.
- יציאה מסוננת היא TCP בלבד. אין UDP, ולכן תוכנית שצריכה אותו לא עובדת, וגם לא יכולה להדליף דרכו.
- DNS תחת
--allow-host. ב-Linux, פתרון שמות בתוך ה-sandbox נכשל, ולכן אין ערוץ DNS החוצה. ב-macOS ה-resolver של המערכת לא מגודר, ולכן נשאר ערוץ DNS ברוחב פס נמוך, אותו פער ש-Anthropic מתעדת ב-sandbox-runtime שלה. - הפרוקסי רואה את ה-host, את הפורט ואת התזמון של כל חיבור. מה שעובר בפנים נשאר בין שני הקצוות. איך בנויה היציאה המסוננת.
- פרטי גישה פנטום מעבירים את האמון אל ai-jail עצמו. עם
--secret, בקשות אל ה-host שנקשר אליו עוברות דרך ai-jail בגלוי כדי שיוכל להכניס את המפתח האמיתי, והוא מצפין אותן מחדש בדרך החוצה. מנהרות לכל host אחר נשארות אטומות. זה מכסה רק לקוחות שיכולים לדבר HTTP רגיל עם הפרוקסי בשביל אותו host. - פרטי גישה שאתם מעבירים עם
--github,--aws,--kube,--gcloudאו--docker-config. קריאה בלבד מגינה על הקובץ מפני שינוי, לא על פרטי הגישה מפני שימוש: כל דבר בתוך ה-sandbox יכול לפעול בשמכם מול השירות הזה כל עוד הם בתוקף. - ב-macOS, הממשק
sandbox-execסומן על ידי Apple כ-deprecated ואינו שקול לבידוד שמתקבל ב-Linux. להבדלים ב-macOS.
מתגים שפותחים את החומה
כל מה שמופיע כאן כבוי עד שתדליקו אותו, חוץ משני מתגים שדלוקים כברירת מחדל: --toolchains ב-Linux ו---agent-state בכל מקום. רובם נותנים לסוכן גישה אמיתית. איך מגדירים אותם מוסבר בעמוד ההגדרה.
הצגת כל המתגים וההשלכות
| מתג | מה כל דבר שרץ ב-sandbox יכול לעשות אחר כך |
|---|---|
--network | לשלוח לכל מקום את כל מה שהוא יכול לקרוא, בלי הגבלה על התעבורה. ברירת המחדל מתירה רק את ה-host של ה-API של סוכן מוכר, וב-Linux גם את ה-registries של החבילות; --allow-host היא החלופה ההדוקה יותר. |
--allow-host | להגיע בדיוק ל-hosts שציינתם ולתת-הדומיינים שלהם, ב-TCP דרך פרוקסי מובנה. כל host ברשימה זוכה לאמון מלא. הרשימה שלכם מתווספת ל-host של ה-API של הסוכן; --no-network עם רשימה נותן לכם את הרשימה הזו בלבד. |
--forward-port | להשתמש במלואו בשירות אחד שיושב על ה-loopback של המארח. כל מה ששרת ה-MCP, ה-socket של העורך או מסד הנתונים שבפורט הזה מקבלים, הסוכן יכול לבקש, בלי רשימת היתרים, בלי בדיקה ובלי רישום ביומן הביקורת. כל שאר הפורטים של המארח נשארים סגורים. רק ב-Linux, ונדחה יחד עם --network. |
--toolchains | דלוק כברירת מחדל ב-Linux. להריץ את ה-toolchains שלכם: ~/.cargo/bin ו-~/.rustup לקריאה בלבד, cache של תלויות ששייך לכלא תחת ~/.local/share/ai-jail/cache/ ושורד בין הרצות, ויציאה מסוננת ל-registries של חבילות. ה-caches האמיתיים של המארח לעולם לא מעוגנים. כבוי תחת --lockdown ועם --no-toolchains. |
--secret | שום דבר חדש. הערך האמיתי של המפתח הזה אף פעם לא נכנס ל-sandbox: הסוכן רואה placeholder, והפרוקסי מחליף אותו בערך האמיתי רק בבקשות HTTP רגיל אל ה-host האחד שקשרתם אותו אליו. התהליך של ai-jail עצמו רואה את הבקשה הזאת בגלוי. |
--audit-log | שום דבר חדש. המתג הזה לא מחליש כלום: הוא כותב שורה אחת לכל הפעלה לקובץ במצב 0600 מחוץ ל-sandbox, כך שהסוכן אף פעם לא רואה אותו. |
--docker | לפעול כ-root על המחשב שלכם דרך ה-daemon של Docker. |
--kvm | לפתוח את /dev/kvm, ממשק הווירטואליזציה של הקרנל, כדי ש-QEMU, Firecracker או האמולטור של Android ירוצו במהירות החומרה. בלינוקס בלבד. האורחים מקבלים רק את הרשת של ה-sandbox, ושום /dev/net/tun לא מועבר. |
--x11 | להקליט הקשות מקלדת ולצלם חלונות X11 אחרים. |
--systemd-user | לבקש מה-user manager של systemd להריץ שירותים על המארח. |
--inherit-env | לקרוא את כל סביבת ה-shell שלכם, כולל הסודות. למפתחות API, --env-from-file מעביר קובץ אחד במקום זה. |
--agent-state | דלוק כברירת מחדל. להשתמש בהתחברות השמורה של הסוכן, למשל ~/.claude של Claude או ~/.local/share/devin ו-~/.config/devin של Devin, מעוגנות לקריאה וכתיבה, ובנוסף מפתח ה-API שלו שמועתק מה-shell שלכם כשהוא מוגדר, כך שהוא עולה מחובר. כל מה שרץ ב-sandbox יכול להשתמש בפרטי הגישה האלה למשך ההרצה. --no-agent-state נותן הרצה מבודדת, ללא התחברות; כבוי תחת --lockdown. |
--github | לפעול בשמכם ב-GitHub. תיקיית ההגדרות של gh מעוגנת לקריאה בלבד, וה-token הפעיל של github.com נקרא מה-keyring של המערכת במארח ומועבר כ-GH_TOKEN להרצה הזאת בלבד, אף פעם לא בשורת הפקודה או בדיסק. קריאה בלבד מגינה על הקובץ, וה-token ממשיך לעבוד כל עוד הוא בתוקף. |
--aws | לפעול בשמכם ב-AWS עם ~/.aws, מעוגן לקריאה בלבד. |
--kube | לפעול בשמכם בכל cluster שב-~/.kube, מעוגן לקריאה בלבד. |
--gcloud | לפעול בשמכם ב-Google Cloud עם ~/.config/gcloud, מעוגן לקריאה בלבד. |
--docker-config | להשתמש בהתחברויות שלכם ל-registries של קונטיינרים שב-~/.docker/config.json, מעוגן לקריאה בלבד. |
--audio | להקליט ולנגן אודיו כל עוד המתג דלוק. |
--no-private-home | לראות את תיקיית הבית האמיתית שלכם. זו חריגה רחבה, ועדיף למפות רק את הנתיב האחד שאתם צריכים. |
כשיש ספק, הוא מסרב לעלות
כלי sandbox שעולה בשקט במצב חלש ממה שביקשתם גרוע מכלי שעוצר עם שגיאה. במקרים האלה ai-jail עוצר.
- קובץ הגדרות שקיים, אבל לא תקין או לא קריא.
- קובץ
.ai-jailשל פרויקט שהוא symlink. כשהקובץ הגלובלי~/.ai-jailהוא symlink, הולכים אחריו רק אם הוא מוביל לקובץ רגיל בבעלותכם, שאף אחד אחר לא יכול לכתוב אליו ושנמצא מחוץ לפרויקט. - הדגל
--lockdownכש-Landlock, כללי מערכת הקבצים של הקרנל עצמו, חסר, חלקי או לא מצליח לחול. גם הצירוף--lockdown --no-landlockנדחה. - הדגל
--allow-tcp-port. הממשק הזה לא מסוגל להגביל UDP, ולכן הדגל מתקבל לצורכי תאימות, ההפעלה נכשלת, והשגיאה מפנה אל--allow-host. --networkיחד עם--allow-host, או--browserעם--allow-host. השניים סותרים זה את זה, ולכן ai-jail מסרב לעלות.--forward-portיחד עם--network, כשה-loopback של המארח כבר נגיש; מחוץ ל-Linux; ולפורט 0 או לפורט מתחת ל-1024, שה-sandbox לא יכול לקשור. ערךforward_portsבטבלת הבסיס של הקובץ הגלובלי נתקל בשני הראשונים בכל הפעלה של דפדפן ובכל הפעלה ב-macOS.- קובץ של
--env-from-fileשהוא symlink, שקריא לקבוצה שלו או לכולם, שבבעלות מישהו אחר או שנמצא בתוך הפרויקט. - ערך של
BWRAP_BINשמצביע על קובץ בינארי של bubblewrap שמישהו שאינו root יכול היה להחליף. הערך לא נלקח בחשבון ומודפסת אזהרה, ואם אין bubblewrap אמין ההפעלה נכשלת. - overlay שלא ניתן להקים: מקור חסר, יעד שחופף למיפוי אחר, או כשל באחסון. מגרסה 2.4 ההפעלה נעצרת במקום ליפול ל-bind רגיל של קריאה וכתיבה על הקבצים האמיתיים.
איך הפרויקט נבדק
מתחזק אחד מנהל את הפרויקט בגלוי. בהמשך מפורט מה נבדק ומה לא.
- 23
- בדיקות אינטגרציה של בריחה מה-sandbox, שמנסות לכתוב אל
/usr, לקרוא את~/.ssh, לקרוא ל-ptraceול-bpf, ומצפות להיכשל - 959
- בדיקות יחידה בעץ המקור בגרסה 2.9.0
- 0
- בקשות רשת בזמן ההפעלה. בדיקת העדכונים היא opt-in, דרך
--update-check
תיקוני אבטחה אחרונים
- 2.9.0:
--x11לבדו מעביר X11 פנימה. קודם, בלי--display, הוא לא עשה כלום, בשקט. - 2.9.0: השמירה האוטומטית כבר לא כותבת לקובץ הפרויקט הפעלות יזומות שהטעינה הלא אמינה דוחה אחר כך, ולכן קובץ שנשמר לא מציג אזהרה בהרצה הבאה.
- 2.9.0: macOS: Codex עולה שוב תחת Seatbelt עם shim פרטי של
psבמקום ה-psה-setuid של המארח. - 2.8.0: macOS: פרופיל ה-seatbelt שומר על ההתחברות של Claude Code ב-Keychain ומתיר את ה-socket של הבקרה של Codex, כך ששניהם עולים כשהם מחוברים.
- 2.8.0: בקשות למיקום הסמן מ-launcher נענות על ידי הטרמינל הווירטואלי, כך ש-launchers כבר לא נתקעים.
- 2.6.2:
--lockdownתמיד מבודד את ה-network namespace, גם עם--network. קודם, הצמד הסותר הזה השאיר את ה-namespace משותף. - 2.6.2: הערכים של
--envו---env-from-fileכבר לא יושבים בשורת הפקודה של ה-launcher, שבה כל תהליך שרץ בשמכם יכול היה לקרוא אותם. הם עוברים דרך קובץ בזיכרון. - 2.4.0: מטא-דאטה של worktree בריפוזיטורי כבר לא יכול לעגן תיקייה שרירותית במארח לקריאה וכתיבה.
- 2.4.0: רשומה כפולה בסביבה כבר לא מכניסה את הערך האמיתי של מפתח
--secretלתוך ה-sandbox. - 2.4.0: פקודת דפדפן בקובץ
.ai-jailלא אמין של פרויקט כבר לא מבטלת lockdown. - 2.4.0:
TIOCSTIמושווה כפי שהקרנל קורא אותו, ב-32 ביט, כך שגרסה עם ביט גבוה כבר לא עוברת את seccomp. - 2.4.0: Landlock מגדר sockets אבסטרקטיים של Unix וסיגנלים בקרנלים 6.12 ומעלה.
- 2.4.0: macOS: הגישה לטרמינל מוגבלת לטרמינלים של ההרצה הזאת, ולא לכל
/dev/ttys*. - 2.4.0: macOS: נתיבים עם
--maskנדחים גם לכתיבה, ולא רק לקריאה. - 2.4.0: מסנן הפלט כבר לא קורס על ESC כפול, שיכול היה להגניב דרכו כתיבה ללוח ההעתקה.
- 2.4.0: פתחי הבדיקה של הפרוקסי לא נכללים יותר ב-builds של release.
איך נוצרת גרסה
- תגיות הגרסה חתומות, וה-CI מאמת כל אחת מהן מול טביעת אצבע של מפתח שמקובעת בריפוזיטורי.
- כל GitHub Action מקובע ל-SHA של commit, וה-actions מוגבלים לרשימת היתרים.
- ה-builds משתמשים ב-toolchain מקובע של Rust ובתלויות עם
--locked. - הקבצים הבינאריים של macOS חתומים ועברו notarization של Apple.
- לכל ארכיון מצורף checksum מסוג SHA-256, ושלב הפרסום בודק אותם שוב.
מה עדיין חסר, לפי מסמך הגרסאות
- סודות החתימה והפרסום עדיין שמורים ברמת הריפוזיטורי, וצריכים לעבור לסביבות ה-release.
- עדיין אין ruleset שמחייב חתימה על כל תגית
v*. - האפשרות immutable releases לא מופעלת.
- הפרסום ל-crates.io עדיין נעשה עם טוקן. מעבר ל-trusted publishing מתוכנן.
- עם מתחזק יחיד, אין בודק שני לשינויים או לגרסאות.
דיווח על פגיעות
אל תפתחו issue ציבורי. השתמשו בדיווח הפרטי על פגיעויות של GitHub, צרפו את שלבי השחזור ואת הגרסאות המושפעות, והשאירו זמן לתיאום התיקון.
שאלות נפוצות
הסוכן יכול לפרוץ החוצה?
בטוח להריץ כשהרשת דלוקה?
--network, כל מה שהסוכן יכול לקרוא הוא יכול גם לשלוח, ו-ai-jail לא נבנה כדי לעצור תוקף נחוש שמנסה לעשות את זה. דרך הביניים היא --allow-host: הסוכן מגיע רק ל-hosts שציינתם, וכל אחד מהם זוכה לאמון מלא. מסכו קודם את הסודות שבתוך הפרויקט. כך ממסכים אותם.הוא עבר ביקורת?
שימו את הסוכן מאחורי סורגים
כל ai-jail הוא קובץ בינארי אחד, בלי daemon ובלי root. מוסיפים מילה אחת לפני הפקודה שאתם מריצים ממילא.
brew tap akitaonrails/tap && brew install ai-jailai-jail claude