דילוג לתוכן

החלפת מפתח ראשי ומפתחות חתימה וגישה לשעת חירום

החליפו את המפתח הראשי שמגן על פרטי גישה שמורים והשתמשו בגישת חירום.

הצגה כ-Markdown

הבקרות בסעיף 14 של 21-compliance.md שאודיטור מבקש בשמן. העמוד הזה מתאר את ההליך; הרשומות שהוא מפיק הן הראיות.

המפתחות

כל פרטי גישה שמורים נאטמים באמצעות מפתח הצפנת נתונים חדש (DEK). המפתח הראשי (KEK) עוטף את ה-DEK, והפניה למפתח הראשי נשמרת לידו (key_ref, או ההפניה שבתוך ערך ארוז). החלפת המפתח הראשי עוטפת מחדש את ה-DEK. היא לעולם אינה מפענחת או מצפינה מחדש פרטי גישה.

הגדרה משמעות
QUIRE_MASTER_KEY המפתח הראשי הנוכחי: 32 bytes, base64. כל secret חדש נעטף באמצעותו
QUIRE_MASTER_KEY_VERSION תווית הגרסה. v1 אם אינה מוגדרת. הגדילו אותה בכל החלפת מפתח
QUIRE_MASTER_KEY_RETIRED מפתחות קודמים שייתכן ש-secrets עדיין עטופים בהם, בפורמט v1=<base64>,v0=<base64>. לקריאה בלבד

שכבת האינטרנט, ה-worker והפקודה bun run kek:rotate קוראים את אותן שלוש הגדרות. עליהן להיות זהות אצל כולם, אחרת אחד מהם לא יוכל לפתוח את מה שאחר חתם.

ללא QUIRE_MASTER_KEY, כל תת-מערכת שומרת את המפתח שהיא מפיקה מתוך QUIRE_SECRET_KEY. זה עובד; עמוד System health מציג מצב degraded, והמידע נשאר קריא גם לאחר הגדרת מפתח ראשי — כך ההחלפה הראשונה מעבירה הכול ממנו. כל מי שיכול לקרוא את סביבת התהליך יכול לפענח כל פרטי גישה שמורים, לכן התקנת production צריכה מפתח ראשי שנשמר במאגר סודות ולא באותו גיבוי של מסד הנתונים.

החלפה

גיל המפתח מוצג ב-Platform console,‏ Security,‏ Master key וגם כמדד quire.secrets.master_key.age (ימים). התזמון היומי platform.key_age (03:41 UTC) כותב תזכורת לשרשרת הביקורת של הפלטפורמה כשהמפתח מגיע לגיל 365 יום, ואז שוב מדי 30 יום עד להחלפתו. החליפו בעקבות התזכורת ובכל פעם שייתכן שמפתח נחשף.

  1. צרו מפתח חדש: openssl rand -base64 32.
  2. הגדירו את QUIRE_MASTER_KEY לערך החדש ואת QUIRE_MASTER_KEY_VERSION לתווית הבאה (v2). העבירו את המפתח הישן אל QUIRE_MASTER_KEY_RETIRED בתור v1=<old base64>. שמרו עותק של שניהם במקום שאינו המארח הזה.
  3. פרסו מחדש את שכבת האינטרנט ואת ה-worker עם ההגדרות החדשות. מעתה secrets חדשים נעטפים תחת env:QUIRE_MASTER_KEY:v2; הישנים עדיין נפתחים באמצעות המפתח שפרש.
  4. בקשו החלפה וציינו את הסיבה שתופיע ביומן הביקורת:
    • במסוף: Security,‏ Master key,‏ Rotate the master key; או
    • במעטפת עם אותה סביבה: bun run kek:rotate request --reason "Annual rotation, ticket SEC-114".
  5. ה-worker עוטף מחדש חלק בכל דקה (לפי התזמון platform.key_rotation) וממשיך לאחר הפעלה מחדש. כדי לסיים בישיבה אחת: bun run kek:rotate run. עקבו באמצעות bun run kek:rotate status.
  6. כשהרשומה מראה שההחלפה הסתיימה עם אפס unresolved ואפס failed, הסירו את המפתח שפרש מ-QUIRE_MASTER_KEY_RETIRED ופרסו מחדש. עד אז השאירו אותו: ערך שלא ניתן היה להעביר עדיין עטוף במפתח הישן.

מה המשימה סורקת

כל מאגר שמכיל DEK עטוף: אלה שמופיעים ב-SEALED_STORES (apps/worker/src/key-rotation.ts). מאגרים של מסד הניהול נסרקים בו; מאגרים של ארגונים נסרקים ארגון אחד בכל פעם, תחת אבטחה ברמת שורה ובמסד שמכיל את הארגון, כך ש-tenant המוצמד למסד ייעודי מוחלף באותו מסד. בדיקה נכשלת אם הסכמה מקבלת עמודת מפתח עטוף שאינה מופיעה ברשימה, ובדיקה אחרת נכשלת אם סקירת פרטי הגישה מסווגת עמודה חתומה שהרשימה החסירה.

הרשומה

  • ops.key_rotation: שורה אחת לכל החלפה, עם הסיבה, מי ביקש אותה, המצב והסיכומים (נעטפו מחדש, כבר עדכני, unresolved, failed).
  • ops.key_rotation_progress: שורה לכל מאגר והיקף לאחר הסריקה, ובה הפניות למפתחות שלא ניתן היה לקרוא ומספר הערכים שהיו תחת כל אחת מהן. החלפה שממשיכים בה מדלגת עליהן.
  • שרשרת הביקורת של הפלטפורמה: platform/key_rotation_request (כולל הסיבה), platform/key_rotation_store אחד לכל מאגר עם הספירות שלו, ו-platform/key_rotation_complete או platform/key_rotation_fail; התזכורת נרשמת כ-platform/key_age_reminder.
  • מדדים: quire.secrets.master_key.age ו-quire.secrets.rewrap.outstanding (ערכים שההחלפה האחרונה לא הצליחה להעביר).

כשיש ערכים שלא נפתרו

ערך שלא נפתר עטוף תחת הפניה למפתח שההתקנה אינה מחזיקה, או שאינו במבנה שהעמודה שלו מצפה לו. רשומת ההתקדמות מציינת את ההפניה (למשל env:QUIRE_MASTER_KEY:v0 (unreadable)). שחזרו את המפתח הזה ל-QUIRE_MASTER_KEY_RETIRED והפעילו החלפה נוספת; אם המפתח אבד לצמיתות, בקשו ממנהל הארגון להזין שוב את פרטי הגישה, והם ייאטמו במפתח הנוכחי. החלפה שנכשלה מציגה את השגיאה ברשומה; תקנו את הגורם ובקשו שוב.

מפתחות חתימה

בנפרד מהמפתח הראשי: כל ארגון חותם על אסימוני OpenID Connect ועל הודעות LTI במפתח RSA משלו, שמפורסם ב-/.well-known/jwks.json. אין כאן צורך בהתערבות מפעיל. התזמון השעתי platform.signing_keys מפרסם מפתח עוקב שבעה ימים לפני שתשעים הימים של המפתח הנוכחי מסתיימים; כעבור שבוע העוקב מתחיל לחתום והמפתח הישן יוצא משימוש; לאחר עוד תשעים יום הישן נמחק ומוסר מקבוצת המפתחות. כל שלב נרשם כ-platform/signing_key_advance בשרשרת הביקורת של הפלטפורמה.

כדי להחליף מוקדם מפתח של ארגון, למשל אחרי חשיפה:

  • במסוף: Security,‏ Master key,‏ Publish a new signing key (נדרשת platform/keys_manage); או
  • במעטפת עם סביבת ה-worker: bun run kek:rotate signing-keys rotate --tenant <slug or id> --reason "Key exposed, INC-3310". הפקודה bun run kek:rotate signing-keys status מפרטת את שלב המפתחות של כל ארגון.

המפתח החדש מתפרסם מיד ומתחיל לחתום אחרי שבעה ימים, כאשר המפתח הנוכחי יוצא משימוש. השבוע הזה מכוון: צדדים מסתמכים שומרים מטמון של קבוצת המפתחות, וחפיפה קצרה יותר תכשיל את כל הכלים בבת אחת. המפתח היוצא נשאר בקבוצת המפתחות עוד תשעים יום, כדי שהאסימונים שכבר חתם עליהם ימשיכו לעבור אימות. אם החשיפה מחייבת להפסיק לבטוח בו מוקדם יותר, מחיקת השורה שלו נעשית על ידי המפעיל בגישה שלו למסד הנתונים ותחת רשומת שינוי (גישת break-glass היא לקריאה בלבד); לאחר מכן אסימונים שנחתמו בו לא יעברו אימות. החלפה כפויה נרשמת עם הסיבה כ-platform/signing_key_rotate בשרשרת הביקורת. ה-worker זקוק לאותן הגדרות QUIRE_MASTER_KEY של שכבת האינטרנט כדי לעטוף את המפתח החדש; bun run kek:rotate של המפתח הראשי עוטף מחדש גם מפתחות חתימה לצד כל השאר (oauth_signing_key נמצא ב-SEALED_STORES).

גישת חירום לסביבת production

לאיש אין גישה קבועה לסביבת production. כשאי אפשר להמתין, בעלים מעניק הרשאת break-glass: Platform console,‏ Security,‏ Break-glass access.

  • להרשאה יש היקף (ארגון אחד או מרשם הפלטפורמה), סיבה של 20 תווים לפחות שמציינת תקרית או כרטיס, וחלון של 5 עד 240 דקות. היא פוקעת בעצמה: בכל statement היא נבדקת מול השעון.
  • אפשר להעניק אותה לבעלים שנתן אותה או לבעלים אחר (נוהל של שני אנשים). רק האדם שקיבל אותה יכול להשתמש בה. להענקה נדרשת platform/break_glass_issue, ולשימוש נדרשת platform/break_glass_use; כברירת מחדל שתיהן מיועדות לבעלים בלבד.
  • Statements עוברים דרך השער, לא באמצעות כניסה למסד: קריאה בלבד, אחד בכל פעם, מוגבלים לארגון או למרשם הניהול, עם timeout של חמש שניות ועד 500 שורות. ערכים בינריים מוצגים לפי הגודל שלהם.
  • שרשרת הביקורת של הפלטפורמה מתעדת הענקה (עם הסיבה), ביטול, כל statement לפני הרצה (platform/break_glass_statement; לניסיון שנדחה נקבעת תוצאה denied) וכל תוצאה (platform/break_glass_result). ops.break_glass_statement שומר את מזהי רשומות הביקורת, כך שרשומת ההענקה מתחברת אליהן.
  • אין אפשרות לכתיבה. שינוי שאינו יכול להמתין לגרסה נעשה מחוץ למוצר דרך גישת המפעיל שלו למסד הנתונים ותחת רשומת שינוי משלו; הרשומה צריכה לציין את הפניית התקרית ששימשה כאן.

למה לא לתת פרטי גישה למסד נתונים: כניסת Postgres שורדת מעבר להפעלה שביקשה אותה, עוקפת את האבטחה ברמת השורה שהיישום מסתמך עליה ואינה יכולה לכתוב לשרשרת הביקורת של המוצר. לכן statements שלה מבוקרים רק עד למקום שבו מישהו שומר את יומן השרת. השער הופך את מסלול הביקורת לחלק מהגישה עצמה, ולא לנוהל סביבה.

כדי להשיב לבקשת ביקורת: הציגו את ההרשאות בתקופה (Break-glass access), פתחו את ההיסטוריה של הרשאה כדי לראות את ה-statements ואת מזהי רשומות הביקורת, וקראו את הרשומות האלה בשרשרת הביקורת של הפלטפורמה (bun run audit:verify --platform מוכיח שהשרשרת שלמה).

ניווט

הקלידו לחיפוש…

↑↓ ניווט↵ בחירהEsc סגירה