החל מגרסה 1.23, Kubernetes לא תומך יותר באימות זהות השרת באמצעות השדה X.509 Common Name (CN) באישורים. במקום זאת, מערכת Kubernetes תסתמך רק על מידע בשדות של שם חלופי לנושא (SAN) מסוג X.509.
כדי למנוע השפעה על האשכולות, צריך להחליף אישורים לא תואמים ללא SAN עבור קצה עורפי של שרתי API מצטברים ו-webhook לפני שמשדרגים את האשכולות ל-Kubernetes גרסה 1.23.
למה Kubernetes לא תומך יותר באישור קצה עורפי ללא SAN
GKE מפעיל Kubernetes בקוד פתוח, שמשתמש ברכיב kube-apiserver כדי ליצור קשר עם קצה העורפי של שרת ה-API המצטבר ועם ה-webhook באמצעות Transport Layer Security (TLS). הרכיב kube-apiserver נכתב בשפת התכנות Go.
לפני גרסה Go 1.15, לקוחות TLS אימתו את הזהות של השרתים שהם התחברו אליהם בתהליך דו-שלבי:
- בודקים אם שם ה-DNS (או כתובת ה-IP) של השרת מופיע כאחד משמות ה-SAN באישור של השרת.
- כפתרון חלופי, בודקים אם שם ה-DNS (או כתובת ה-IP) של השרת שווה לשם הנפוץ (CN) באישור של השרת.
בשנת 2011, RFC 6125 הוציא משימוש את אימות זהות השרת שמבוסס על השדה CN. דפדפנים ואפליקציות אחרות שקריטיות לאבטחה כבר לא משתמשים בשדה הזה.
כדי להתאים למערכת האקולוגית הרחבה יותר של TLS, Go 1.15 הסירה את שלב 2 מתהליך האימות שלה, אבל השאירה מתג לניפוי באגים (x509ignoreCN=0) כדי לאפשר את ההתנהגות הישנה כדי להקל על תהליך ההעברה. Kubernetes version 1.19 הייתה הגרסה הראשונה שנבנתה באמצעות Go 1.15. באשכולות GKE בגרסאות 1.19 עד 1.22, מתג הניפוי באגים מופעל כברירת מחדל כדי לתת ללקוחות יותר זמן להחליף את האישורים עבור ה-webhook המושפע והקצה העורפי המצטבר של שרת ה-API.
Kubernetes version 1.23 מבוססת על Go 1.17, שבה הוסר מתג הניפוי באגים. אחרי ש-GKE ישדרג את האשכולות לגרסה 1.23, לא תהיה אפשרות להתחבר מהמישור של האשכול לשירותי API מצטברים או ל-webhook שלא מספקים אישור X.509 תקף עם SAN מתאים.
זיהוי אשכולות מושפעים
לצבירי תגים שמופעלות בהם גרסאות תיקון לפחות 1.21.9 או 1.22.3
באשכולות בגרסאות 1.21.9 ו-1.22.3 ואילך עם Cloud Logging מופעל, GKE מספק יומן של Cloud Audit Logs כדי לזהות קריאות לשרתי קצה מושפעים מהאשכול. כדי לחפש את היומנים, אפשר להשתמש במסנן הבא:
logName =~ "projects/.*/logs/cloudaudit.googleapis.com%2Factivity"
resource.type = "k8s_cluster"
operation.producer = "k8s.io"
"invalid-cert.webhook.gke.io"
אם האשכולות שלכם לא קראו לשרתי קצה עם אישורים מושפעים, לא תראו יומנים. אם יומן ביקורת כזה קיים, הוא יכלול את שם המארח של קצה העורף המושפע.
בדוגמה הבאה מוצגת רשומה ביומן עבור קצה עורפי של webhook שאותו מארח שירות בשם example-webhook במרחב השמות default:
{
...
resource {
type: "k8s_cluster",
"labels": {
"location": "us-central1-c",
"cluster_name": "example-cluster",
"project_id": "example-project"
}
},
labels: {
invalid-cert.webhook.gke.io/example-webhook.default.svc: "No subjectAltNames returned from example-webhook.default.svc:8443",
...
},
logName: "projects/example-project/logs/cloudaudit.googleapis.com%2Factivity",
operation: {
...
producer: "k8s.io",
...
},
...
}
שמות המארחים של השירותים המושפעים (למשל example-webhook.default.svc) נכללים כסיומות בשמות התוויות שמתחילות ב-invalid-cert.webhook.gke.io/. אפשר גם לקבל את שם האשכול שביצע את השיחה מהתווית resource.labels.cluster_name, שהערך שלה בדוגמה הזו הוא example-cluster.
תובנות לגבי הוצאה משימוש
אפשר לראות באילו אשכולות נעשה שימוש באישורים לא תואמים בתובנות לגבי הוצאה משימוש. התובנות זמינות עבור אשכולות שפועלת בהם גרסה 1.22.6-gke.1000 ואילך.
גרסאות אחרות של אשכולות
אם יש לכם אשכול בגרסת תיקון שקודמת ל-1.22.3 בגרסה המשנית 1.22, או בגרסת תיקון שקודמת ל-1.21.9, יש לכם שתי אפשרויות לקבוע אם האשכול שלכם מושפע מהוצאה משימוש הזו:
אפשרות 1 (מומלצת): משדרגים את האשכול לגרסת תיקון שכוללת תמיכה בזיהוי של אישורים מושפעים באמצעות יומנים. מוודאים ש-Cloud Logging מופעל באשכול. אחרי השדרוג של האשכול, יומני Cloud Audit Logs שמזהים את הבעיה יופקו בכל פעם שהאשכול ינסה להתקשר לשירות שלא מספק אישור עם SAN מתאים. היומנים יופקו רק בניסיון לבצע שיחה, ולכן מומלץ להמתין 30 יום אחרי השדרוג כדי לאפשר מספיק זמן להפעלת כל נתיבי השיחות.
מומלץ להשתמש ביומנים כדי לזהות את השירותים שהושפעו, כי הגישה הזו מצמצמת את המאמץ הידני על ידי יצירת יומנים באופן אוטומטי כדי להציג את השירותים שהושפעו.
אפשרות 2: בודקים את האישורים שמשמשים את ה-Webhooks או את שרתי ה-API המצטברים באשכולות כדי לדעת אם הם מושפעים מכך שאין להם SAN:
- מקבלים את הרשימה של Webhooks ושל שרתי API מצטברים באשכול ומזהים את ה-backends שלהם (שירותים או כתובות URL).
- בודקים את האישורים שבהם נעשה שימוש בשירותי הקצה העורפי.
בשיטה הזו נדרשת עבודה ידנית כדי לבדוק את כל האישורים, ולכן כדאי להשתמש בה רק אם אתם צריכים להעריך את ההשפעה של הוצאת השימוש משימוש ב-Kubernetes גרסה 1.23 לפני שאתם משדרגים את האשכול ל-גרסה 1.21. אם אפשר לשדרג את האשכול לגרסה 1.21, כדאי לשדרג אותו קודם ואז לפעול לפי ההוראות באפשרות 1 כדי להימנע ממאמץ ידני.
זיהוי שירותים לקצה העורפי לבדיקה
כדי לזהות את הקצה העורפי שעשוי להיות מושפע מהוצאה משימוש, צריך לקבל את רשימת ה-Webhooks ושירותי ה-API המצטברים ואת הקצה העורפי המשויך שלהם באשכול.
כדי לראות רשימה של כל ה-webhook הרלוונטיים באשכול, משתמשים בפקודות kubectl הבאות:
kubectl get mutatingwebhookconfigurations -A # mutating admission webhooks
kubectl get validatingwebhookconfigurations -A # validating admission webhooks
כדי לקבל שירות או כתובת URL משויכים של קצה עורפי עבור webhook נתון, צריך לבדוק את clientConfig.service
השדה או את webhooks.clientConfig.url
השדה בהגדרות של ה-webhook:
kubectl get mutatingwebhookconfigurations example-webhook -o yaml
הפלט של הפקודה הזו אמור להיראות כך:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
webhooks:
- admissionReviewVersions:
clientConfig:
service:
name: example-service
namespace: default
port: 443
שימו לב שאפשר לציין את הקצה העורפי של clientConfig כשירות Kubernetes (clientConfig.service) או ככתובת URL (clientConfig.url).
כדי לפרסם את כל שירותי ה-API הרלוונטיים המצטברים באשכול, משתמשים בפקודה kubectl הבאה:
kubectl get apiservices -A |grep -v Local # aggregated API services
הפלט של הפקודה הזו אמור להיראות כך:
NAME SERVICE AVAILABLE AGE
v1beta1.metrics.k8s.io kube-system/metrics-server True 237d
בדוגמה הזו, הפונקציה מחזירה את השירות metric-server ממרחב השמות kube-system.
כדי לקבל שירות משויך ל-API מצטבר נתון, צריך לבדוק את השדה spec.service:
kubectl get apiservices v1beta1.metrics.k8s.io -o yaml
הפלט של הפקודה הזו אמור להיראות כך:
...
apiVersion: apiregistration.k8s.io/v1
kind: APIService
spec:
service:
name: metrics-server
namespace: kube-system
port: 443
בדיקת האישור של שירות
אחרי שמזהים את שירותי ה-Backend הרלוונטיים לבדיקה, אפשר לבדוק את האישור של כל שירות ספציפי, כמו example-service:
מוצאים את הסלקטור ואת יעד ההעברה של השירות:
kubectl describe service example-serviceהפלט של הפקודה הזו אמור להיראות כך:
Name: example-service Namespace: default Labels: run=nginx Selector: run=nginx Type: ClusterIP IP: 172.21.xxx.xxx Port: 443 TargetPort: 444בדוגמה הזו, ל-
example-serviceיש את הסלקטורrun=nginxואת יציאת היעד444.חיפוש פוד שתואם לסלקטור:
kubectl get pods --selector=run=nginxהפלט של הפקודה אמור להיראות כך:
NAME READY STATUS RESTARTS AGE example-pod 1/1 Running 0 21mהגדרת העברה ליציאה אחרת
מ-
kubectllocalhost אל ה-Pod.kubectl port-forward pods/example-pod LOCALHOST_PORT:TARGET_PORT # port forwarding in backgroundמחליפים את מה שכתוב בשדות הבאים בפקודה:
-
LOCALHOST_PORT: הכתובת להאזנה. -
TARGET_PORTTargetPortמשלב 1.
-
משתמשים ב-
opensslכדי להדפיס את האישור שבו השירות משתמש:openssl s_client -connect localhost:LOCALHOST_PORT </dev/null | openssl x509 -noout -textבדוגמה הזו של פלט מוצג אישור תקין (עם רשומות SAN):
Subject: CN = example-service.default.svc X509v3 extensions: X509v3 Subject Alternative Name: DNS:example-service.default.svcבדוגמה הזו של פלט מוצג אישור שחסר בו SAN:
Subject: CN = example-service.default.svc X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication X509v3 Authority Key Identifier: keyid:1A:5F:29:D8:E9:3C:54:3C:35:CC:D8:AB:D1:21:FD:C3:56:25:C0:74כדי להפסיק את העברת היציאה ברקע, מריצים את הפקודות הבאות:
$ jobs [1]+ Running kubectl port-forward pods/example-pod 8888:444 & $ kill %1 [1]+ Terminated kubectl port-forward pods/example 8888:444
בדיקת האישור של קצה עורפי של כתובת URL
אם ה-webhook משתמש ב-url
backend,
הוא מתחבר ישירות לשם המארח שצוין בכתובת ה-URL. לדוגמה, אם כתובת ה-URL היא https://example.com:123/foo/bar, משתמשים בפקודה openssl הבאה כדי להדפיס את האישור שבו נעשה שימוש בשרת העורפי:
openssl s_client -connect example.com:123 </dev/null | openssl x509 -noout -text
צמצום הסיכון בשדרוג לגרסה 1.23
אחרי שמזהים את האשכולות המושפעים ואת שירותי ה-Backend שלהם באמצעות אישורים ללא SAN, צריך לעדכן את ה-Webhooks ואת שרתי ה-API המצטברים של ה-Backend כדי להשתמש באישורים עם SAN מתאים לפני שמשדרגים את האשכולות לגרסה 1.23.
מערכת GKE לא תשדרג אוטומטית אשכולות בגרסאות 1.22.6-gke.1000 ואילך עם קצה עורפי שמשתמשים באישורים לא תואמים, עד שתחליפו את האישורים או עד שגרסה 1.22 תגיע לסוף התמיכה הרגילה.
אם האשכול שלכם הוא בגרסת GKE קודמת ל-1.22.6-gke.1000, אתם יכולים למנוע באופן זמני שדרוגים אוטומטיים על ידי הגדרה של החרגה מתחזוקה כדי למנוע שדרוגים משניים.
משאבים
למידע נוסף על השינוי הזה, כדאי לעיין במקורות המידע הבאים:
- הערות לגבי הגרסה Kubernetes 1.23
- Kubernetes מבוסס על Go 1.17. בגרסה הזו של Go, אי אפשר להשתמש בהגדרת הסביבה
GODEBUG=x509ignoreCN=0כדי להפעיל מחדש התנהגות מדור קודם שהוצאה משימוש, שלפיה ה-CN של אישורי שרת X.509 נחשב כשם מארח.
- Kubernetes מבוסס על Go 1.17. בגרסה הזו של Go, אי אפשר להשתמש בהגדרת הסביבה
- הערות לגבי הגרסאות של Kubernetes 1.19
ושל
Kubernetes 1.20
- התנהגות מדור קודם שהוצאה משימוש, שבה השדה CN באישורי שרת מסוג X.509 נחשב כשם מארח כשלא קיימים שמות SAN, מושבתת עכשיו כברירת מחדל.
- נתוני הגרסה של Go 1.17
- הסימון הזמני
GODEBUG=x509ignoreCN=0הוסר.
- הסימון הזמני
- נתוני הגרסה של Go 1.15
- התנהגות מדור קודם שהוצאה משימוש, שלפיה השדה CN באישורי X.509 נחשב למארח כשאין SAN, מושבתת עכשיו כברירת מחדל.
- RFC 6125
(page 46)
- השימוש בערך CN הוא נוהג קיים, אבל הוא הוצא משימוש. רצוי שרשויות אישורים יספקו ערכים של
subjectAltNameבמקום זאת.
- השימוש בערך CN הוא נוהג קיים, אבל הוא הוצא משימוש. רצוי שרשויות אישורים יספקו ערכים של
- Admission webhooks