
GitHub Actions בלי מפתחות AWS קבועים: OIDC מנפיק הרשאות קצרות

כדי לפרוס מ־GitHub Actions ל־AWS בלי לשמור מפתחות גישה קבועים, מגדירים ב־IAM ספק OIDC ותפקיד שהאמון בו מוגבל לריצות מהמאגר ומהענף המורשים. ה־workflow מבקש אסימון זהות עבור הריצה, ו־AWS STS מחליף אותו באישורים זמניים לתפקיד, כפי שמתואר ב הנחיות GitHub לחיבור עם AWS.
לשם כך דרושים גם id-token: write ב־workflow וגם תנאי אמון בצד AWS. ההרשאה הראשונה מאפשרת לבקש אסימון OIDC; היא אינה מעניקה בפני עצמה כתיבה למשאבי AWS. מדיניות האמון קובעת מי רשאי לקבל את התפקיד, ומדיניות ההרשאות שמוצמדת לתפקיד קובעת מה הריצה תוכל לעשות לאחר מכן. צריך לצמצם את שתיהן לפני שמסירים מפתחות שמורים.
מוסיפים את GitHub כספק זהות ב־IAM
בחשבון AWS שאליו מיועדת הפריסה, הוסיפו ב־IAM ספק זהות מסוג OpenID Connect. כתובת הספק היא https://token.actions.githubusercontent.com, וה־audience הרגיל לפעולה הרשמית הוא sts.amazonaws.com. זהו ספק הזהות של GitHub; שם המאגר והענף יופיעו בהמשך במדיניות האמון של התפקיד, ולא במקום כתובת הספק.
צרו תפקיד IAM שישמש את הפריסה ושמרו את ה־ARN שלו עבור ה־workflow. אם פריסות שונות מגיעות לחשבונות AWS שונים או דורשות גישה למשאבים שונים, תפקיד נפרד לכל פריסה מאפשר לקבוע לכל אחת תנאי אמון והרשאות משלה. בדוגמה שלהלן ACCOUNT_ID הוא מזהה חשבון AWS, ואילו ORG, REPO, ORG_ID ו־REPO_ID הם מצייני מקום לזהות הארגון והמאגר ב־GitHub.
מגבילים את האמון למאגר ולענף המדויקים
תנאי sub הוא גבול הגישה החשוב במדיניות האמון: הוא מתאר את מקור הריצה שמבקשת להניח את התפקיד. תיעוד IAM של AWS ממליץ להגביל אותו למאגר או לענף מוגדרים ומבהיר ש־IAM דורש תנאי sub שאינו ריק ואינו תו כללי בלבד. לצורך פריסה מענף main בלבד, השתמשו בהתאמה מדויקת של aud ושל sub.
במאגר שה־sub שלו כולל מזהים קבועים, מדיניות אמון לדוגמה היא: {"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"Federated":"arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"},"Action":"sts:AssumeRoleWithWebIdentity","Condition":{"StringEquals":{"token.actions.githubusercontent.com:aud":"sts.amazonaws.com","token.actions.githubusercontent.com:sub":"repo:ORG@ORG_ID/REPO@REPO_ID:ref:refs/heads/main"}}}]}. החליפו את כל מצייני המקום לפני שמירת המדיניות. StringEquals דורש התאמה מלאה, ולכן sub של ענף אחר לא יתאים לתנאי הזה.
התאימו את ערך sub לצורה שהמאגר שלכם מנפיק: מאגרים ותיקים שלא עברו למזהים קבועים משתמשים בתבנית repo:ORG/REPO:ref:refs/heads/main. אם ה־job משתמש ב־GitHub Environment, ה־sub מציין את שם הסביבה במקום את תבנית הענף. במקרה כזה הגבילו בהגדרות ה־Environment אילו ענפים רשאים לפרוס אליו, כי התאמה לשם הסביבה לבדה אינה מסננת ענפים. שגיאת התאמה היא סיבה לבדוק את ערך ה־sub הצפוי, ולא להרחיב את התנאי ל־repo:ORG/REPO:*: תבנית כזאת עשויה לאפשר גם לריצות מענפים או מהקשרים אחרים באותו מאגר להניח את התפקיד.
מצמידים לתפקיד הרשאות פריסה בלבד
מדיניות האמון אינה מעניקה הרשאה לפעול ב־S3, ב־ECR או בשירות AWS אחר. הצמידו לתפקיד מדיניות הרשאות לפי פקודות הפריסה והמשאבים שאליהם הן ניגשות. בדוגמה מותנית, job שרק מעלה קבצים לנתיב מסוים ב־S3 זקוק להרשאת העלאה לאובייקטים באותו נתיב; פריסה שמוחקת קבצים או מעדכנת שירות תדרוש פעולות נוספות שיש להגדיר במפורש.
עברו על פקודות הפריסה לפני מתן ההרשאות. תנאי אמון צר מונע מריצה לא מורשית לקבל את התפקיד, אך אינו מגביל את מה שריצה מורשית יכולה לעשות באמצעותו. אם נותנים לתפקיד גישה רחבה לכל המשאבים בחשבון, גם פריסה תקינה תקבל כוח פעולה רחב מהנדרש. אפשר לעדכן את מדיניות ההרשאות בנפרד ממדיניות האמון כאשר תהליך הפריסה משתנה.
מחברים את ה־workflow לתפקיד
בקובץ תחת .github/workflows הגדירו טריגר לפריסה מענף main והוסיפו ל־job את ההרשאה id-token: write. אם הוא מריץ actions/checkout, הוסיפו גם contents: read. הוראות הפעולה aws-actions/configure-aws-credentials מציגות שימוש ב־OIDC באמצעות role-to-assume ו־aws-region; בדוגמה שלהן מופיעה גרסת הפעולה v6.3.0.
- הגדירו ל־workflow אירוע push המוגבל לענף main. ודאו שהענף הזה תואם לערך sub שבמדיניות האמון; סינון הענף בטריגר אינו מחליף את התנאי בצד AWS.
- הגדירו ב־permissions של ה־job את id-token: write, ובמידת הצורך contents: read. אם הגדרתם permissions מפורשות, כללו בהן את ההרשאות שהשלבים ב־job אכן צריכים.
- אחרי checkout, הוסיפו שלב שמשתמש ב־aws-actions/[email protected]. בשדות with הגדירו role-to-assume כ־ARN של התפקיד ו־aws-region כאזור שבו פועלת הפריסה.
- הוסיפו אחריו את פקודות הפריסה. אל תעבירו לפעולה aws-access-key-id או aws-secret-access-key מתוך GitHub Secrets: אלה קלטים למסלול הזדהות שמשתמש במפתחות שמורים.
ה־ARN מפנה לתפקיד המבוקש, אך עצם כתיבתו ב־workflow אינה מקנה גישה אליו. AWS בודקת את אסימון הריצה מול ספק הזהות ותנאי האמון של אותו תפקיד. לכן שינוי של role-to-assume בקובץ ה־workflow אינו עוקף לבדו את התנאים שהוגדרו ב־IAM.
בודקים ריצה מורשית ודחייה בלי לבצע פריסה
התחילו ב־job אבחון שאין בו פקודות שמשנות משאבים. מיד אחרי שלב הגדרת האישורים הריצו aws sts get-caller-identity, ובדקו שהחשבון וה־ARN של התפקיד בפלט הם אלה שהתכוונתם אליהם. כך אפשר לבדוק את מסלול ההזדהות לפני שמוסיפים אליו פקודות פריסה.
כדי לבדוק דחייה של ענף אחר, הפעילו ממנו job אבחון עם אותם permissions ואותו role-to-assume, אך ללא פקודות פריסה וללא מפתחות שמורים. אם ה־workflow המקורי מופעל רק מ־main, נדרש לצורך הניסוי טריגר אבחון שמסוגל לפעול גם מענף הבדיקה; אין צורך להרחיב לשם כך את מדיניות האמון ב־AWS. בתצורה המוגבלת ל־main, ניסיון ההזדהות מהענף האחר אמור להיכשל לפני הרצת פקודות AWS. כשמשתמשים ב־Environment, בדקו במקום זאת שכללי ההגנה שלו מונעים מענף לא מורשה להגיע ל־job הפריסה.
לאחר שהריצה המורשית מקבלת את התפקיד והריצה הלא מורשית נדחית, הסירו את מפתחות הגישה הקבועים מה־workflow ומ־GitHub Secrets ברמת המאגר, הארגון או ה־Environment, לפי המקום שבו נשמרו. אם אלה מפתחות IAM שנוצרו במיוחד לפריסה הזאת, בטלו אותם גם ב־AWS לאחר שווידאתם שאין תהליך אחר שתלוי בהם. הריצו שוב את הפריסה ללא הסודות כדי לוודא שהיא נשענת על מסלול OIDC שהגדרתם.
קראו גם:
כתבות קשורות


Docker Secrets לא מצפין קובץ מקומי — כך מונעים מסוד להישאר בתמונה

n8n באחסון עצמי: audit אחד חושף webhooks לא מוגנים

GitHub או Bitbucket: 50 דקות CI בחינם משנות את הבחירה

Claude Code או GitHub Copilot: החיוב משנה את עלות הסוכן

AWS Activate או Microsoft for Startups: האשראי הגדול לא מגיע ביום הראשון
הירשמו לניוזלטר שלנו
קבלו את החדשות האחרונות על Web3, AI וקריפטו ישירות לתיבת הדואר.