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

כדי שסיסמה או מפתח לא יישארו בתמונת Docker, ספקו אותם לפקודה הזקוקה להם בזמן הבנייה באמצעות BuildKit secret mount, במקום להכניס את הערך ל־ARG או ל־ENV. תיעוד סודות הבנייה של Docker מתאר mount שזמין למשך הוראת הבנייה שצורכת אותו. התנאי הוא שאותה הוראה לא תעתיק את הסוד לקובץ בתמונה ולא תדפיס אותו לפלט.
אם היישום זקוק לסוד אחרי שהקונטיינר עולה, מספקים אותו בזמן הריצה. ב־Compose מקומי, הנחיות OWASP ל־Docker מסבירות שסוד שמקורו בקובץ מותקן באמצעות bind mount, ולכן הכרזת secret אינה מצפינה את קובץ המקור במארח. ב־Swarm הסודות מנוהלים ומוצפנים בתעבורה וביומן הניהול, אך גם יצירת סוד מנוהל אינה מצפינה בדיעבד את קובץ הקלט המקומי.
בזמן הבנייה: לחשוף את הסוד להוראה שצריכה אותו
BuildKit secret mount מתאים לבנייה שצריכה להזדהות מול מאגר חבילות פרטי או שירות חיצוני. כברירת מחדל, הוראת RUN רואה את הסוד כקובץ תחת /run/secrets בזמן שהיא פועלת; ההוראות הבאות אינן מקבלות את אותו mount אוטומטית. שימוש ב־ENV בתוך Dockerfile משאיר ערך בסביבת התמונה, ושימוש ב־ARG עבור סוד עלול לחשוף אותו בהיסטוריית הבנייה או במטא־דאטה. לכן אין להעביר את הערך דרך אחת מההוראות האלה.
- שמרו את הסוד בקובץ מחוץ למאגר ול־build context, למשל /secure/app_token כאשר מריצים את הבנייה מתוך תיקיית הפרויקט. הנתיב הוא דוגמה; ודאו גם שהקובץ אינו נכנס לתמונה דרך COPY או ADD ושגישה אליו במארח מוגבלת.
- ב־Dockerfile השתמשו, למשל, בהוראה RUN --mount=type=secret,id=app_token,required=true test -s /run/secrets/app_token. זוהי בדיקת זמינות בלבד. בבנייה האמיתית מחליפים את test בפקודה שקוראת את הקובץ בלי להדפיס את תוכנו ובלי לשמור עותק שלו.
- הריצו docker build --secret id=app_token,src=/secure/app_token -t demo:secret . . מזהה הסוד app_token חייב להתאים למזהה בהוראת RUN. האפשרות required=true גורמת להוראה להיכשל אם הסוד לא סופק.
ה־mount מונע שמירה אוטומטית של קובץ הסוד בתמונה; הוא אינו מבטל פעולות של הכלי שקרא אותו. מנהל חבילות, למשל, עשוי לכתוב קובץ הגדרות עם אישור הגישה, ופקודת אבחון עשויה להדפיס אותו ללוג הבנייה. אם ערך נכתב לשכבת תמונה, מחיקת הקובץ בהוראת RUN מאוחרת אינה מתקנת את השכבה שכבר נוצרה. יש למנוע את הכתיבה עצמה ולבנות את התמונה מחדש.
ב־Compose מקומי: להגביל את השירות ולהגן על הקובץ
ב־Compose מגדירים את מקור הסוד ברמה העליונה ומעניקים גישה במפורש רק לשירותים שצריכים אותו. מפרט הסודות של Compose מאפשר לקחת את הערך מקובץ או ממשתנה סביבה במארח; מקור מסוג environment נתמך ב־Docker Compose, אך לא ב־docker stack deploy. בחירה במשתנה כמקור אינה מחייבת להעביר את הערך לשדה environment של השירות עצמו.
- הניחו קובץ כגון /secure/app_token מחוץ למאגר הקוד והגבילו את הקריאה בו למי שמפעיל את Compose. אם הדיסק או הגיבויים עשויים לחשוף את הקובץ, הגנו גם על האחסון המתאים בהצפנה ובהרשאות. .gitignore מונע צירוף רגיל למאגר, אך אינו מגן על קובץ שכבר נמצא במארח או על עותק גיבוי שלו.
- ב־compose.yaml הגדירו תחת secrets את app_token עם file: /secure/app_token, וברשימת secrets של השירות המורשה הוסיפו את app_token. היישום צריך לקרוא את /run/secrets/app_token. אם הוא תומך במשתנה שמפנה לקובץ, אפשר להעביר בו את הנתיב; אין להציב בו את תוכן הסוד.
- אחרי docker compose up -d, בדקו בשירות המורשה שהקובץ קיים וקריא בלי להציג את תוכנו. בשירות שלא קיבל הרשאה בדקו שהנתיב אינו קיים. בדקו גם ב־docker inspect של הקונטיינר שהערך אינו מופיע ב־Config.Env; נתיב המקור עשוי להופיע בפרטי ה־mount, ולכן יש לשקול גם את רגישות הנתיב.
אם מקור הסוד הוא משתנה סביבה במארח, בדקו מי יוצר אותו ומי יכול לקרוא אותו במערכת ההרצה. ערך שמופיע בפקודת shell, בפלט של כלי פריסה או בלוג עדיין עלול להיחשף, גם אם השירות מקבל אותו כקובץ. בדקו את הפלט של docker compose config לפני שיתוף או שמירה שלו, במיוחד כאשר קובץ ההגדרות משתמש בהרחבת משתנים.
ב־Swarm: למסור סוד מנוהל לשירות מורשה
שירות Swarm מקבל סוד רק כאשר הוענקה לו גישה מפורשת. במערכת Linux, הסוד המפוענח מותקן כברירת מחדל תחת /run/secrets במערכת קבצים בזיכרון למשך פעולת המשימה המורשית. זהו מנגנון אחר מ־Compose מקומי: ההצפנה בתעבורה וביומן Raft חלה על הסוד שמנהל Swarm, בעוד קובץ שממנו נקרא הערך במארח עדיין דורש טיפול נפרד.
- במנהל Swarm צרו סוד באמצעות docker secret create app_token /secure/app_token. הפעולה קוראת את תוכן הקובץ ויוצרת סוד מנוהל; היא אינה מוחקת את קובץ הקלט. השאירו עליו את ההרשאות והגנת האחסון הדרושות, או טפלו בו לפי מדיניות מחיקת הסודות שלכם.
- לצורך המחשה בלבד, צרו שירות עם הרשאה לסוד באמצעות docker service create --name secret_demo --secret app_token alpine sleep infinity. ביישום אמיתי השירות קורא את /run/secrets/app_token. העתקת התוכן למשתנה סביבה של הקונטיינר תחזיר את הערך למשטח החשיפה שהמעבר לסוד נועד לצמצם.
- בדקו ב־docker service inspect secret_demo שהסוד הוקצה לשירות, וב־docker secret inspect app_token שהתצוגה כוללת מטא־דאטה בלי את תוכן הסוד. בדקו גם שמשימה של שירות שלא קיבל את app_token אינה רואה את הקובץ.
לפני פרסום: לבדוק היסטוריה, מטא־דאטה ושכבות
לבדיקת דליפה השתמשו בבניית ניסיון עם ערך בדיקה ייחודי, לא עם סיסמה פעילה. אחרי הבנייה הריצו docker history --no-trunc demo:secret וחפשו את הערך בפקודות השכבות; אחר כך בדקו ב־docker image inspect demo:secret את Config.Env ושדות המטא־דאטה. אם הערך אינו מופיע שם, עדיין ייתכן שכלי שהופעל ב־RUN כתב אותו לקובץ בתוך שכבה.
כדי לבדוק גם את השכבות, שמרו עותק מקומי באמצעות docker image save -o image.tar demo:secret, פתחו את הארכיון וסרקו את קובצי התצורה ואת ארכיוני השכבות אחר ערך הבדיקה. מציאת הערך דורשת תיקון של הפקודה שכתבה אותו ובנייה מחדש לפני פרסום. חיפוש טקסטואלי לא יזהה כל קידוד, שינוי או נגזרת של הסוד, ולכן יש לבחון גם אילו קבצים ופלטים מייצר הכלי שצרך אותו.
בסביבת הריצה בדקו שהערך אינו מופיע ב־docker inspect או בלוגים, בלי להדפיס סוד פעיל כחלק מהבדיקה. ב־Compose השלימו את הבדיקה בהרשאות ובהצפנת האחסון של קובץ המקור; ב־Swarm בדקו את הקצאת הסוד לשירותים. כל אחת מהבדיקות מכסה מקום אחר שבו הערך עלול להישאר: הוראת הבנייה, שכבת התמונה, סביבת הקונטיינר, פלט היישום או המארח.
קראו גם:
כתבות קשורות


אסימון API של Cloudflare דלף? Roll משאיר את אותן ההרשאות

Docker או Podman: הביצועים כמעט זהים, ההרשאות לא

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

מפתח דלף ב־GitHub? מחיקתו מהקוד אינה סוגרת את האירוע

Cloud Run או Fargate: אפס שרתים פעילים עולה בזמן תגובה
הירשמו לניוזלטר שלנו
קבלו את החדשות האחרונות על Web3, AI וקריפטו ישירות לתיבת הדואר.