תיעוד 107
אימות זהות הפעלה בסביבות באירוח עצמי
אמתו את ה-JWT שב-
CLAUDE_CODE_SESSION_ACCESS_TOKENכדי ששירותים ברשת שלכם יוכלו לבטוח בבקשות מהפעלות בסביבה שלכם באירוח עצמי.
הערה: סביבות באירוח עצמי נמצאות בבטא ציבורית בתוכניות Team ו-Enterprise. בעלים (Owner) מפעיל אותן על ידי הפעלת Allow self-hosted environments בדף הניהול Cloud environments. דף זה עוסק באימות זהות הפעלה. ראו את מדריך ההתחלה המהירה להגדרה ואת פריסה לסביבת ייצור (Deploy to production) למתכוני פריסת צי (fleet).
סביבה באירוח עצמי מאפשרת להפעלות של Claude Code באינטרנט לרוץ על גבי תשתית שאתם מפעילים במקום על גבי זו של Anthropic. מכיוון שההפעלה רצה בתוך הרשת שלכם, Claude יכול לקרוא ישירות לשירותים הפנימיים שלכם. שירותים אלה זקוקים לדרך לוודא שבקשה הגיעה מהפעלת Claude Code בסביבה שלכם, ולזהות את המשתמש או את זהות השירות שיצרו את אותה הפעלה.
כל הפעלה בסביבה באירוח עצמי מקבלת JSON Web Token (JWT) חתום במשתנה הסביבה CLAUDE_CODE_SESSION_ACCESS_TOKEN. הפעלה מציגה את הטוקן כמו כל אישור bearer. לדוגמה, סקריפט ש-Claude מריץ יכול לקרוא לשירות שלכם באמצעות curl -H "Authorization: Bearer $CLAUDE_CODE_SESSION_ACCESS_TOKEN". חברת Anthropic חותמת על הטוקן ומפרסמת את מפתחות האימות בנקודת קצה ציבורית של JWKS. השירותים שלכם מושכים את המפתחות האלה, מאמתים את החתימה, וקוראים את הטענות (claims) כדי להחליט איזו גישה להעניק.
#טוקן ההפעלה
לפני שתכתבו קוד אימות, דעו מה הטוקן מבסס ומהו המבנה שספריית ה-JWT שלכם תראה.
#מה הטוקן מוכיח
טוקן תקף מבסס עובדות מסוימות, ובאופן מכוון אינו מבסס אחרות:
- מוכיח: ש-Anthropic הנפיקה את הטוקן עבור הפעלה ספציפית בסביבה ספציפית, וכיצד נוצרה ההפעלה: על ידי משתמש בארגון שלכם, או על ידי זהות שירות של הארגון שלכם, שזו הדרך שבה מתחילות הפעלות ערוץ Claude Tag.
- לא מוכיח: איזה תהליך במארח ה-runner מציג אותו. הטוקן נמצא במשתנה סביבה בתוך ההפעלה, כך שכל קוד ש-Claude מריץ, וכל כלי או שרת MCP שההפעלה מפעילה, יכולים לקרוא ולהציג אותו.
שתי השלכות על השירותים שלכם:
- אמתו את טענת
audמול מזהה הסביבה שלכם, ערך ה-ccpool_...המוצג לצד הסביבה שלכם בדף הניהול Cloud environments, כדי לדחות טוקנים שהונפקו לסביבה של ארגון אחר כלשהו. - הגבילו את היקף ההרשאות שאתם גוזרים מהטוקן למה שהפעלת קידוד בודדת אמורה להיות מסוגלת לעשות, ולא לכל מה שיוצר ההפעלה יכול לעשות. ראו הגבלת היקף של אישורים נגזרים.
#מבנה הטוקן
הערך של CLAUDE_CODE_SESSION_ACCESS_TOKEN כולל קידומת sk-ant-cc- ולאחריה JWT סטנדרטי בעל שלושה חלקים:
sk-ant-cc-<base64url header>.<base64url payload>.<base64url signature>הסירו את הקידומת לפני העברת הערך לספריית JWT. טוקנים שמונפקים להפעלות ענן באירוח של Anthropic נושאים במקום זאת קידומת sk-ant-si- ונחתמים על ידי קבוצת מפתחות שונה, לכן דחו כל ערך שאינו מתחיל ב-sk-ant-cc-.
אלגוריתם החתימה הוא ES256, שהוא ECDSA על עקומת P-256 עם SHA-256. כותרת הטוקן נושאת kid שמזהה איזה מפתח ב-JWKS חתם עליו.
#אימות הטוקן
האימות מתבצע באחד משני מקומות: שירותים ברשת שלכם מאמתים את הטוקן באופן קריפטוגרפי מול המפתחות המפורסמים של Anthropic, וסקריפטי מעטפת בתוך ההפעלה יכולים להשתמש במפענח המובנה של הקובץ הבינארי של ה-runner במקום זאת.
#אימות הטוקן מהשירות שלכם
חברת Anthropic מפרסמת את מפתחות האימות בנקודת קצה ציבורית וללא צורך באימות:
https://api.anthropic.com/v1/code/.well-known/jwks.jsonהתגובה היא JSON Web Key Set סטנדרטי. חברת Anthropic מחליפה את מפתחות החתימה מעת לעת, ומפתחות מלפני החלפה נשארים בקבוצה מספיק זמן כדי שטוקנים שנחתמו על ידם ימשיכו לעבור אימות, לכן אל תקבעו מפתח בודד. נקודת הקצה מגדירה Cache-Control: public, max-age=300, כך ששמירת קבוצת המפתחות במטמון ומשיכתה מחדש כל חמש דקות היא בטוחה.
אמתו כל טוקן נכנס מול הבדיקות הבאות:
- בדקו את הקידומת: דחו את הערך אם הוא אינו מתחיל ב-
sk-ant-cc-, ולאחר מכן הסירו את הקידומת הזו. השארית היא JWT קומפקטי סטנדרטי. - אמתו את החתימה: משכו את ה-JWKS, בחרו את המפתח שה-
kidשלו תואם לכותרת הטוקן, ואמתו את חתימת ה-ES256. דחו טוקנים שכותרת ה-algשלהם אינהES256. אם מגיע טוקן עםkidשאינו נמצא בקבוצת המפתחות שבמטמון שלכם, משכו מחדש את ה-JWKS פעם אחת לפני דחייתו: לאחר החלפת מפתחות, טוקנים חדשים נחתמים במפתח שעדיין אינו קיים בקבוצה שבמטמון שלכם. - אמתו את המנפיק: דחו את הטוקן אם
issאינו בדיוקccr. - אמתו את קהל היעד מול הסביבה שלכם: טענת
audהיא מערך. דחו את הטוקן אלא אם כן הוא מכיל את מזהה הסביבה שלכם, שהוא במבנהccpool_.... מזהה הסביבה מוצג בחלונית פרטי הסביבה שלכם בדף הניהול Cloud environments, ומופיע כטענתccr:pool_idבכל אחד מטוקני ההפעלה של הסביבה. בדיקה זו היא מה שמגביל את הטוקן לסביבה שלכם ודוחה טוקנים שהונפקו לארגונים אחרים. - אמתו את התפקיד: דחו את הטוקן אם
ccr:roleאינו בדיוקsession_worker. טוקנים אחרים המונפקים עבור סביבות באירוח עצמי, כגון סודות סביבה, טוקנים של runner והזמנות עבודה (work orders), נחתמים על ידי אותה קבוצת מפתחות אך נושאים תפקידים שונים. - אמתו תפוגה: דחו את הטוקן אם
expנמצא בעבר. חברת Anthropic מנפיקה טוקני הפעלה עם משך חיים של ארבע שעות כברירת מחדל ומקסימום שמונה שעות. ה-runner מרענן את הטוקן לפני תפוגה ודוחף את הערך החדש אל ההפעלה, כך שתהליכי משנה ש-Claude מפעיל לאחר רענון יורשים אותו. לכן, הפעלה אחת יכולה להציג מספר טוקנים תקפים שונים לשירות שלכם לאורך חיי ההפעלה שלה. - קראו את הזהות: זהות המשתמש היוצר נמצאת בטענת
act:act.subהוא מזהה משתמש ה-Anthropic שלו במבנה עם הקידומתuser:<id>, ו-act.email, כאשר ממשק היצירה רשם כזה, הוא כתובת האימייל שלו. הפעלות שזהות שירות של הארגון שלכם יוצרת, כולל הפעלות ערוץ Claude Tag, נושאות במקום זאת subject מסוגagent:, לכן התייחסו להפעלה ככזו שנוצרה על ידי משתמש רק כאשרact.subנושא את הקידומתuser:, במקום לבדוק האם טענות זהות נעדרות. ראו את סימוכין לטענות למבנה המלא ולטענות הכפולות השטוחות.
הבדיקות ממופות ישירות לספריות JWT סטנדרטיות. הדוגמאות להלן מיישמות את הרצף המלא ב-Node.js עם jose, שמטפלת במשיכת JWKS, שמירה במטמון ובבחירת kid, וב-Python עם PyJWT ולקוח ה-JWKS המובנה שלה.
#Node.js (jose)
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://api.anthropic.com/v1/code/.well-known/jwks.json")
);
const PREFIX = "sk-ant-cc-";
const EXPECTED_POOL_ID = "ccpool_...";
export async function verifySessionToken(raw: string) {
if (!raw.startsWith(PREFIX)) {
throw new Error("not a self-hosted runner session token");
}
const jwt = raw.slice(PREFIX.length);
const { payload } = await jwtVerify(jwt, JWKS, {
issuer: "ccr",
audience: EXPECTED_POOL_ID,
algorithms: ["ES256"],
});
if (payload["ccr:role"] !== "session_worker") {
throw new Error("token is not a session_worker token");
}
const act = payload.act as { email?: string; sub?: string };
return {
sessionId: payload["ccr:session_id"] as string,
poolId: payload["ccr:pool_id"] as string,
orgId: payload["ccr:org_id"] as string,
creatorEmail: act?.email,
creatorSub: act?.sub,
};
}#Python (PyJWT)
import jwt
from jwt import PyJWKClient
JWKS_URL = "https://api.anthropic.com/v1/code/.well-known/jwks.json"
PREFIX = "sk-ant-cc-"
EXPECTED_POOL_ID = "ccpool_..."
jwks = PyJWKClient(JWKS_URL)
def verify_session_token(raw: str) -> dict:
if not raw.startswith(PREFIX):
raise ValueError("not a self-hosted runner session token")
token = raw.removeprefix(PREFIX)
signing_key = jwks.get_signing_key_from_jwt(token)
payload = jwt.decode(
token,
signing_key.key,
algorithms=["ES256"],
issuer="ccr",
audience=EXPECTED_POOL_ID,
)
if payload.get("ccr:role") != "session_worker":
raise ValueError("token is not a session_worker token")
act = payload.get("act") or {}
return {
"session_id": payload["ccr:session_id"],
"pool_id": payload["ccr:pool_id"],
"org_id": payload["ccr:org_id"],
"creator_email": act.get("email"),
"creator_sub": act.get("sub"),
}#אימות הטוקן בתוך ההפעלה
סקריפטי מעטפת רצים בתוך ההפעלה, לפני ש-Claude מתחיל. במקום לקרוא לספריית JWT, הם יכולים להריץ את תת-הפקודה self-hosted-runner decode-token של הקובץ הבינארי של ה-runner. תת-הפקודה קוראת את הטוקן מארגומנט מיקומי, מתוך CLAUDE_CODE_SESSION_ACCESS_TOKEN, או מ-stdin בצינור (piped stdin), בסדר זה, ולאחר מכן מסירה את הקידומת, מאמתת את החתימה מול נקודת הקצה של ה-JWKS, בודקת תפוגה, ומדפיסה את הטענות כ-JSON. תת-הפקודה מבצעת את בדיקות החתימה והתפוגה בלבד; היא אינה בודקת את iss, aud או ccr:role. כאשר החלטת האימות של סקריפט המעטפת שלכם תלויה בטענות אלו, קראו אותן מה-JSON המודפס והשוו אותן באופן מפורש.
פקודה זו מחלצת את זהות היוצר, תוך העדפת ה-subject של ספק ה-SSO, לאחר מכן את כתובת האימייל, ולאחר מכן את ה-subject של היוצר ב-act.sub, user:<id> או agent:<id>:
"$CLAUDE_RUNNER_CLAUDE_BIN" self-hosted-runner decode-token | jq -re '.act.attested_by.sub // .act.email // .act.sub'סקריפטי מעטפת מקבלים את הנתיב המוחלט לקובץ הבינארי של ה-runner עצמו ב-CLAUDE_RUNNER_CLAUDE_BIN. השתמשו בנתיב זה במקום ב-claude שנמצא לפי ה-PATH, כדי שהפענוח ירוץ על אותו קובץ בינארי שה-runner עצמו משתמש בו.
השתמשו ב-jq -re במקום ב-jq -r כדי שטענה חסרה תגרום ליציאה בקוד שאינו אפס. עם -r בלבד, טענה חסרה מדפיסה את המחרוזת המילולית null ומסתיימת בקוד אפס, מה שמעביר בשקט ערך לא תקין בהמשך השרשרת. העבירו את --no-verify ל-decode-token רק עבור בדיקה לא מקוונת (offline) כאשר אין גישה לנקודת הקצה של ה-JWKS.
#סימוכין לטענות
הטבלה להלן מפרטת את טענות טוקן ההפעלה הרלוונטיות לאימות. קראו זהות מתוך מרחב השמות ccr:* ומתוך שרשרת act. הטענות השטוחות account_email, organization_uuid ו-account_uuid הן כפילויות לצורכי תאימות לאחור שייתכן שיוסרו בעתיד. הפעלות שזהות שירות של הארגון שלכם יוצרת, כולל הפעלות ערוץ Claude Tag, נושאות subject מסוג agent: ב-act.sub ומשמיטות את act.email, ccr:account_id, account_email ו-account_uuid. שתי טענות האימייל הן אופציונליות גם עבור הפעלות שנוצרו על ידי משתמש: Anthropic רושמת אותן בעת יצירת ההפעלה רק כאשר אישורי הגישה של הבקשה היוצרת נושאים אימייל, והפעלה שנשלחה מה-CLI יכולה להיות חסרה את שתיהן, לכן בססו את הזיהוי על act.sub או על ccr:account_id במקום על אימייל. טוקנים יכולים גם לשאת טענות נוספות מעבר לטבלה זו; התעלמו מטענות שאינכם מזהים.
| טענה | סוג | תיאור |
|---|---|---|
iss | string | תמיד ccr. |
sub | string | ccr:session:<session_id>. |
aud | array of strings | מכיל תמיד את anthropic-api. עבור הפעלות בסביבות באירוח עצמי המערך מכיל גם את מזהה הסביבה שלכם, כגון ccpool_.... אמתו את מזהה הסביבה, ולא את anthropic-api. |
exp | number | תפוגה כחותמת זמן של Unix. משך חיים של ארבע שעות כברירת מחדל, מקסימום שמונה שעות. |
iat | number | זמן הנפקה כחותמת זמן של Unix. |
jti | string | מזהה ייחודי של הטוקן. |
ccr:role | string | תמיד session_worker עבור טוקני הפעלה. |
ccr:session_id | string | מזהה ההפעלה. אותו ערך כמו הסיומת של sub. |
ccr:pool_id | string | מזהה הסביבה שלכם. אותו ערך שמופיע ב-aud. |
ccr:org_id | string | מזהה ארגון ה-Anthropic שלכם. |
ccr:account_id | string | מזהה חשבון ה-Anthropic של המשתמש היוצר: הערך של act.sub ללא הקידומת user:, מזהה מתויג user_.... אותו ערך שמועבר ב-CLAUDE_RUNNER_ACCOUNT_ID של ה-hook של spawn-runner וש-[--lock-to-account](/docs/en/self-hosted-environments-reference#runner-cli-flags) מקבל, כך ששלושתם משתווים כמחרוזות שוות. |
account_email | string | שכפול של act.email; נעדר בכל פעם ש-act.email נעדר. |
organization_uuid | string | ה-UUID של ארגון ה-Anthropic שלכם. |
account_uuid | string | ה-UUID של חשבון ה-Anthropic של המשתמש היוצר. |
act | object | שרשרת האצלת סמכויות לפי RFC 8693. ראו שרשרת ה-act. |
#שרשרת ה-act
טענת act מתעדת את נתיב האצלת הסמכויות המלא מהמשתמש או מזהות השירות שיצרו את ההפעלה ועד לסביבה שהסוד שלה אישר את ה-runner, ואת הזהות שיצרה את אותו סוד. היוצר הוא הגורם החיצוני ביותר, ולכן act.sub מזהה אותו ישירות.
| נתיב | תיאור |
|---|---|
act.sub | מזהה משתמש ה-Anthropic של המשתמש היוצר, במבנה user:<id>, או agent:<id> כאשר זהות שירות של הארגון שלכם יצרה את ההפעלה, כפי שקורה עבור הפעלות ערוץ Claude Tag. |
act.email | כתובת האימייל של המשתמש היוצר, כאשר נרשמה כזו בעת יצירת ההפעלה. אל תדרשו אותה; בססו את המפתח על act.sub. |
act.attested_by | אישור ספק הזהות במעלה הזרם עבור המשתמש היוצר, כאשר זמין. act.attested_by.sub הוא ה-subject שספק ה-SSO שלכם, כגון Google או Okta, הנפיק. העדיפו זאת על פני act.email בעת מיפוי לזהויות במערכות שלכם. |
act.act | ה-runner שהפעיל את ההפעלה. act.act.sub הוא ccr:runner:<runner_id>. |
act.act.act | הסביבה. act.act.act.sub הוא ccr:pool:<pool_id>. |
act.act.act.act | הזהות שיצרה את סוד הסביבה שאיתו נרשם ה-runner. השרשרת מסתיימת כאן. |
#הגבלת היקף של אישורים נגזרים
טוקן ההפעלה מזהה את המשתמש או את זהות השירות שיצרו את ההפעלה, אך אל תתייחסו אליו כשווה ערך לכך שאותו יוצר התחבר ישירות. הטוקן נמצא במשתנה סביבה בתוך ההפעלה, כך שכל קוד ש-Claude מריץ, וכל כלי או שרת MCP שההפעלה מפעילה, יכולים לקרוא ולהציג אותו.
האימות הוא גם לא מקוון: טוקן שעובר אימות מול ה-JWKS נשאר תקף עד ל-exp שלו, ללא קשר למה שקרה להפעלה מאז, ו-Anthropic אינה מפרסמת ערוץ ביטולים (revocation feed) עבור טוקני הפעלה. הגבילו כל דבר שאתם גוזרים מהטוקן בהתאם לכך.
כאשר השירות שלכם מחליף את הטוקן באישורי גישה פנימיים, הנפיקו אישורים המוגבלים בהיקפם למה שהפעלת קידוד אחת אמורה להגיע אליו:
- הגבילו יכולות: העניקו הרשאות קריאה וכתיבה למשאבים שההפעלה צריכה עבור משימות קידוד, ולא יכולות ניהוליות שהיוצר מחזיק בהן במקומות אחרים.
- הגבילו את משך החיים: הגבילו אישורים נגזרים ל-
expשל הטוקן, או לזמן קצר יותר. - בצעו ביקורת לפי ההפעלה: תעדו את
ccr:session_idואתjtiלצד זהות היוצר כדי שתוכלו להתחקות אחר פעולות עד להפעלה ספציפית.
#משתני סביבה קשורים
זהות היוצר מופיעה גם במשתני סביבה רגילים בשני ממשקים שלעולם אינם מאמתים את הטוקן:
- ה-hook של
spawn-runner, במתזמר: ה-hook רץ לפני שקיים runner כלשהו עבור הפעלה שנמצאת בתור, ומקבל את זהות היוצר במשתנים כגוןCLAUDE_RUNNER_ACCOUNT_EMAILו-CLAUDE_RUNNER_ACCOUNT_ID. המתזמר קורא אותם מהזמנת העבודה (work order), הטוקן החתום לשימוש חד פעמי שמאשר הפעלת runner אחד, מבלי לאמת בעצמו את החתימה של הזמנת העבודה; הטענות נחשבות מהימנות מכיוון שהזמנת העבודה מגיעה דרך החיבור של המתזמר ל-Anthropic, שסוד הסביבה מאמת. - סקריפטי מעטפת, בתוך ההפעלה: סקריפטי מעטפת מקבלים את
CCR_SESSION_ACCOUNT_EMAIL, כתובת האימייל של היוצר שחולצה מראש מהטוקן ללא אימות חתימה. המשתנה מתאים לתיוג, כגון טריילרים של commit, ולא להחלטות אימות והרשאה.
השתמשו במשתנים הרגילים להחלטות בצד המתזמר כגון בחירת תמונת מכונה (machine image). השתמשו ב-CLAUDE_CODE_SESSION_ACCESS_TOKEN כאשר שירות במורד הזרם זקוק להוכחה קריפטוגרפית עצמאית במקום לבטוח בסביבה של ה-runner.
#מה הלאה
- סביבות באירוח עצמי: מודל הסביבה, ה-runner וההפעלה; מדריך ההתחלה המהירה ופריסה לסביבת ייצור מכילים הגדרה ותפעול.
- התאמה אישית של הפעלות: סקריפטי מעטפת שצורכים את הטוקן, וה-hook של
spawn-runner. - סימוכין: דגלי CLI, משתני סביבה ומדדים (metrics).