במאמר הזה מוסבר איך לפרוס משאב של שער Kubernetes שמוגדר להעברת TLS, כדי לאפשר ניתוב מבוסס-SNI (ציון שם השרת) ב-Google Kubernetes Engine (GKE). הגישה הזו שומרת על הצפנה מקצה לקצה ועל Mutual TLS (mTLS) בין לקוחות לבין Pods של קצה עורפי, תומכת בפרוטוקולים מבוססי-TLS שאינם HTTP וחוסכת כתובות IP.
המסמך הזה מיועד למומחי Cloud Architect, למומחי רשתות ולמפתחים שמתכננים, פורסים ומנהלים רשתות מוצפנות לעומסי עבודה בקונטיינרים. למידע נוסף על תפקידים נפוצים ומשימות לדוגמה, קראו את המאמר תפקידים ומשימות נפוצים של משתמשי GKE.
סקירה כללית
בנוסף למאזני עומסים של אפליקציות (ALB) מסוג HTTP ו-HTTPS בשכבה 7, בקר השער של GKE תומך במאזני עומסי רשת של שרתי proxy בשכבה 4 (מאזני עומסים של שרתי proxy מסוג TCP) שמוגדרים עם העברת TLS ללא שינוי.
במעבר TLS, מאזן העומסים לא מסיים או מפענח את סשן ה-TLS. במקום זאת, כשלקוח מתחיל לחיצת יד בפרוטוקול TLS, מאזן העומסים בודק את הודעת ה-ClientHello הלא מוצפנת כדי לקרוא את שם המארח של ציון שם השרת (SNI). על סמך שם המארח של SNI, מאזן העומסים מנתב את זרם ה-TCP הגולמי המוצפן לשירות Kubernetes המתאים בקצה העורפי, שבו קבוצות ה-Pod של האפליקציה מסיימות את ה-TLS ישירות.
כשמשתמשים ב-TLS passthrough עם Gateway API, אפשר:
- שמירה על הצפנה מקצה לקצה ותמיכה בתאימות לארכיטקטורת אבטחה ללא אמון (Zero Trust): התנועה נשארת מוצפנת באופן מלא מהלקוח ל-Pod בקצה העורפי, בלי לתת למאזן העומסים גישה למפתחות פרטיים או לנתונים בטקסט פשוט.
- שימוש באימות TLS בו-זמני (mTLS): אימות לקוחות ישירות בשכבת האפליקציה בקצה העורפי.
- חיסכון בכתובות IPv4: ריבוב של כמה שירותי TLS נפרדים בדומיינים או בתת-דומיינים שונים, באמצעות כתובת IP ויציאה אחת של שער (כמו
443). - תמיכה בפרוטוקולים מבוססי-TLS שאינם HTTP: ניתוב תעבורת נתונים מוצפנת לעומסי עבודה עם שמירת מצב, למסדי נתונים, לשירותי gRPC בהתאמה אישית או לפרוטוקולים קנייניים שפועלים ב-TLS.
GatewayClasses נתמכים
ה-GatewayClasses הבאים של GKE תומכים במאזינים של TLS passthrough ובמשאבי TLSRoute:
gke-l7-regional-external-managedgke-l7-regional-external-managed-mcgke-l7-rilbgke-l7-rilb-mcgke-l7-cross-regional-internal-managed-mc
להשוואה מלאה של היכולות של GatewayClass, ראו היכולות של GatewayClass.
לפני שמתחילים
לפני שמתחילים, חשוב לוודא שביצעתם את המשימות הבאות:
- מפעילים את Google Kubernetes Engine API. הפעלת Google Kubernetes Engine API
- כדי להשתמש ב-Google Cloud CLI למשימה הזו, מתקינים ואז מאתחלים את ה-CLI של gcloud. אם כבר התקנתם את ה-CLI של gcloud, הריצו את הפקודה
gcloud components updateכדי להוריד את הגרסה העדכנית. יכול להיות שגרסאות קודמות של ה-CLI של gcloud לא תומכות בהרצת הפקודות שבמסמך הזה.
- מוודאים שיש לכם אשכול קיים במצב Autopilot או Standard, שפועל ב-GKE בגרסה 1.35 ואילך, וש-Gateway API מופעל. כדי ליצור אשכול, קראו את המאמר יצירת אשכול Autopilot. כדי להפעיל את Gateway API באשכול קיים, קראו את המאמר הפעלת Gateway API באשכול.
- מוודאים שכלי שורת הפקודה OpenSSL מותקן. אתם משתמשים בכלי הזה כדי ליצור אישורי TLS עם חתימה עצמית לאפליקציות לדוגמה.
הדרישות של GKE Gateway Controller
- אפשר להשתמש ב-Gateway API רק באשכולות מותאמים ל-VPC.
- אם אתם משתמשים ב-GatewayClasses אזוריים או חוצי-אזורים, אתם צריכים להפעיל רשת משנה של שרתי proxy בלבד.
- התוסף
HttpLoadBalancingצריך להיות מופעל באשכול. - אם אתם משתמשים ב-Istio, אתם צריכים לשדרג את Istio לאחת מהגרסאות הבאות:
- 1.15.2 ואילך
- 1.14.5 ואילך
- 1.13.9 ואילך.
- אם אתם משתמשים ב-VPC משותף, בפרויקט המארח אתם צריכים להקצות את התפקיד
Compute Network Userלחשבון השירות של GKE בפרויקט השירות.
הגבלות ומגבלות
כשפורסים שערים של מעבר TLS ומשאבי TLSRoute, חשוב לשים לב למגבלות הבאות:
- כשמגדירים את
protocol: TLSבמאזין, צריך להגדיר במפורש את השדהtls.modeלערךPassthrough. GKE לא תומך במצב ברירת המחדלTerminateלמאזינים שלTLS. - במאזין
TLS, אל תגדירו את השדהcertificateRefs. אימות אישור לקוח ברמת השער (השדהspec.tls.frontendValidation) לא חל על מאזינים מסוגTLS. כדי להשתמש ב-mTLS, צריך לאמת את אישורי הלקוח בקצה העורפי. - מאזין
TLSיכול לשתף יציאה רק עם מאזיניTLSאחרים. אי אפשר להגדיר מאזינים מסוגHTTPאוHTTPSבאותה יציאה כמו מאזין מסוגTLS. - בשדה
TLSRoute.spec.hostnames, צריך לציין רשימה של 1 עד 1,024 שמות מארחים של SNI (שם מארח מדויק או קידומת של תו כללי לחיפוש, כמו*.example.com). כל רשומה יכולה להכיל עד 253 תווים ולא יכולה להיות כתובת IP. - בשדה
TLSRoute.spec.rulesאפשר להגדיר כלל אחד לכל היותר. - כל כלל תומך בערך אחד בדיוק של
backendRef. בקצוות עורפיים של שרתי proxy בשכבה 4 אי אפשר לפצל את התנועה לפי משקל בין כמה קצוות עורפיים. - ב-GatewayClasses של אשכול יחיד יש תמיכה רק בקצוות עורפיים של שירותים, וב-GatewayClasses של כמה אשכולות יש תמיכה רק בקצוות עורפיים של ServiceImport.
- בשדה
TLSRoute.spec.rules.backendRefs, מגדירים את השדהportליציאה שהוצהרה על ידי משאב היעד Service או ServiceImport, ולא ליציאת הקונטיינר. - שירות ה-Kubernetes לקצה העורפי (כולל שירותים שיוצאו כ-ServiceImport) שאליו מפנה משאב TLSRoute צריך להשתמש בערך
TCPלפרוטוקול היציאה שלו. - אי אפשר לצרף משאב BackendTLSPolicy, שרת proxy לאימות זהויות (IAP) או מדיניות אבטחה של Google Cloud Armor לקצה עורפי של TLSRoute, כי הבקר דוחה הגדרות מהסוג הזה.
- כשמצרפים משאב GCPBackendPolicy לשירות או ל-ServiceImport שמשאב TLSRoute מפנה אליו, צריך להגדיר את השדה
spec.protocolלערךTCP(אם משמיטים את השדהspec.protocol, ברירת המחדל היא הערךHTTP, שרלוונטי רק לקצוות עורפיים של HTTPRoute). אפשר לצרף ל-Service או ל-ServiceImport מדיניותHTTPאחת ומדיניותTCPאחת בו-זמנית. אם כמה כללי מדיניות עם אותו ערךspec.protocolמטרגטים את אותו Service או ServiceImport, המדיניות הישנה ביותר מקבלת קדימות וכללי המדיניות החדשים יותר לא מצורפים. משאב GCPBackendPolicy עם השדה
spec.protocolשמוגדר כ-TCPתומך רק בשדות הבאים בשדהspec.default(כשמטרגטים משאב ServiceImport, אפשר להגדיר את שלושת השדות האחרונים לכל אשכול, אזור או אזור גיאוגרפי בשדהspec.default.scopesבמקום זאת):timeoutSecconnectionDraininglogging.enabledוגםlogging.sampleRate-
sessionAffinity.type(הערכים התקינים הםCLIENT_IPאוNONE) capacityScalerPercentbalancingMode: CONNECTIONmaxConnectionsPerEndpoint
אי אפשר ליצור מדיניות
TCPשכוללת שדות אחרים, או מדיניותHTTPשכוללת שדות שרלוונטיים רק ל-TCP(balancingMode: CONNECTIONו-maxConnectionsPerEndpoint). שרת ה-API של Kubernetes דוחה את המדיניות בזמן היצירה. מידע נוסף זמין במאמר הגדרת משאבי Gateway באמצעות כללי מדיניות.אם גם משאב HTTPRoute וגם משאב TLSRoute מפנים לאותו Service או ServiceImport, צירוף של משאב HealthCheckPolicy לאותו יעד מחליף את בדיקות התקינות הנפרדות שמוגדרות כברירת מחדל, ומחיל את אותה הגדרה של בדיקת תקינות על הקצוות העורפיים של שני המסלולים. כדי להגדיר בדיקות תקינות באופן עצמאי (למשל בדיקות תקינות של
HTTPלקצוות עורפיים של HTTPRoute ובדיקות תקינות שלSSLאוTCPלקצוות עורפיים של TLSRoute), צריך ליצור שירות נפרד לכל נתיב ולצרף משאב HealthCheckPolicy לכל שירות. שני השירותים יכולים לבחור את אותם Pods.תכונות של שכבה 7 (כמו שכתוב של כתובות URL, ניתוב מבוסס-נתיב וטרנספורמציות של כותרות HTTP) לא חלות על מאזני עומסים של שרתי proxy ל-TCP בשכבה 4.
הגדרת רשת משנה לשרתי proxy בלבד
שער אזורי דורש רשת משנה מסוג proxy-only (purpose=REGIONAL_MANAGED_PROXY) בכל אזור שבו פורסים אותו. תת-רשת אחת של שרתי proxy בלבד משרתת את כל מאזני העומסים האזוריים שמבוססים על Envoy באותו אזור. אם באזור שלכם עדיין אין רשת משנה כזו, תוכלו לקרוא איך להגדיר רשת משנה מסוג proxy-only.
שמירת כתובת IP סטטית
לפני שפורסים את השער, צריך להקצות כתובת IP סטטית אזורית כדי שכתובת השער לא תשתנה, וכדי שתוכלו לפתור את שמות המארחים לדוגמה. הוראות ליצירת כתובת IP סטטית מופיעות במאמר יצירת שער HTTP(S) חיצוני אזורי.
פריסת שער עם מאזינים להעברת TLS
השער בקטע הזה מגדיר שני מאזינים של TLS שמשתפים את היציאה 443. כל מאזין תואם לשם מארח SNI אחר ומעביר חיבורים במצב מעבר למשאבי TLSRoute מצורפים.
שומרים את המניפסט הבא בתור
store-gateway.yaml:kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: store-tls spec: gatewayClassName: gke-l7-regional-external-managed listeners: # Accept TLS connections for store.example.com without decrypting them. - name: store protocol: TLS port: 443 hostname: "store.example.com" tls: mode: Passthrough allowedRoutes: kinds: - kind: TLSRoute # A second listener on the same port, selected by a different SNI name. - name: store-german protocol: TLS port: 443 hostname: "store-de.example.com" tls: mode: Passthrough allowedRoutes: kinds: - kind: TLSRoute addresses: - type: NamedAddress value: IP_ADDRESS_NAMEמחליפים את IP_ADDRESS_NAME בשם של כתובת ה-IP הסטטית ששמרתם.
שימו לב שלבלוק
tlsאין שדהcertificateRefs. במצבPassthrough, אישור ה-TLS נמצא ב-Pods של הקצה העורפי ולא במאזן העומסים.מחילים את המניפסט של Gateway:
kubectl apply -f store-gateway.yamlמוודאים שהשער מתוכנת:
kubectl get gateway store-tlsהקצאת מאזן העומסים נמשכת כמה דקות. הפלט אמור להיראות כך:
NAME CLASS ADDRESS PROGRAMMED AGE store-tls gke-l7-regional-external-managed 35.118.32.224 True 53s
שימוש בשער פנימי
כדי שתעבורת הנתונים תישאר בתוך רשת ה-VPC, משנים את הערך של השדה gatewayClassName ל-gke-l7-rilb ומסירים את השדה addresses מהמניפסט של השער. לאחר מכן, GKE יוצר מאזן עומסי רשת פנימי אזורי לשרת proxy ומקצה כתובת IP פנימית מרשת המשנה של האשכול. שאר השלבים בדף הזה לא השתנו. שולחים את בקשות הבדיקה ממכונה של Compute Engine באותה רשת VPC, כי אי אפשר להגיע לכתובת ה-IP הפנימית מהאינטרנט.
פריסת אפליקציות TLS לדוגמה
בקטע הזה פורסים שני פריסות NGINX (store-v1 ו-store-german) שמסיימות את ה-TLS ביציאה 8443. כל פריסה מציגה אישור TLS אחר ומדווחת על שם המארח של ה-SNI שהיא קיבלה, וכך אתם יכולים לוודא שהניתוב התבצע על סמך SNI.
יוצרים אישור TLS בחתימה עצמית ומפתח פרטי לכל שם מארח:
openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \ -keyout store.key -out store.crt \ -subj '/CN=store.example.com' \ -addext 'subjectAltName=DNS:store.example.com' openssl req -x509 -newkey rsa:2048 -nodes -sha256 -days 365 \ -keyout store-de.key -out store-de.crt \ -subj '/CN=store-de.example.com' \ -addext 'subjectAltName=DNS:store-de.example.com'בסביבת הייצור, השתמשו באישורים מרשות האישורים שלכם. אישורים בחתימה עצמית מאפשרים לדוגמה הזו להיות עצמאית.
כדי לאחסן כל אישור ומפתח ב-Kubernetes Secret, מריצים את הפקודות הבאות:
kubectl create secret tls store-cert \ --cert=store.crt --key=store.key kubectl create secret tls store-de-cert \ --cert=store-de.crt --key=store-de.keyשומרים את המניפסט הבא בתור
store-tls-app.yaml:apiVersion: v1 kind: ConfigMap metadata: name: store-nginx data: default.conf: | server { listen 8443 ssl; ssl_certificate /etc/nginx/ssl/tls.crt; ssl_certificate_key /etc/nginx/ssl/tls.key; default_type text/plain; # Report which Pod answered and which SNI name it received. return 200 "backend: $hostname\nsni: $ssl_server_name\n"; } --- apiVersion: apps/v1 kind: Deployment metadata: name: store-v1 spec: replicas: 2 selector: matchLabels: app: store-v1 template: metadata: labels: app: store-v1 spec: containers: - name: nginx image: nginx:1.27 ports: - containerPort: 8443 volumeMounts: - name: config mountPath: /etc/nginx/conf.d - name: cert mountPath: /etc/nginx/ssl readOnly: true volumes: - name: config configMap: name: store-nginx - name: cert secret: secretName: store-cert --- apiVersion: v1 kind: Service metadata: name: store-v1 spec: selector: app: store-v1 ports: - port: 443 targetPort: 8443 --- apiVersion: apps/v1 kind: Deployment metadata: name: store-german spec: replicas: 2 selector: matchLabels: app: store-german template: metadata: labels: app: store-german spec: containers: - name: nginx image: nginx:1.27 ports: - containerPort: 8443 volumeMounts: - name: config mountPath: /etc/nginx/conf.d - name: cert mountPath: /etc/nginx/ssl readOnly: true volumes: - name: config configMap: name: store-nginx - name: cert secret: secretName: store-de-cert --- apiVersion: v1 kind: Service metadata: name: store-german spec: selector: app: store-german ports: - port: 443 targetPort: 8443בשירותים לא צריך שדה
appProtocol. GKE מעביר את התנועה של TLSRoute לקצה העורפי כ-TCP, כך שלשדה אין השפעה.מחילים את המניפסט על האשכול:
kubectl apply -f store-tls-app.yamlמוודאים שה-Pods פועלים:
kubectl get pods -l 'app in (store-v1,store-german)'הפלט אמור להיראות כך:
NAME READY STATUS RESTARTS AGE store-german-7d9f8c5b47-2xqkd 1/1 Running 0 41s store-german-7d9f8c5b47-h6tzn 1/1 Running 0 41s store-v1-6c4b9d8f65-kp8vr 1/1 Running 0 41s store-v1-6c4b9d8f65-wm2ls 1/1 Running 0 41s
פריסת משאבי TLSRoute לניתוב מבוסס-SNI
משאב TLSRoute מתחבר למאזין של Gateway ומנתב חיבורים שתואמים לשמות המארחים שלו לשירות קצה עורפי יחיד.
שומרים את המניפסט הבא בתור
store-tls-routes.yaml:kind: TLSRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: store spec: parentRefs: - kind: Gateway name: store-tls sectionName: store hostnames: - "store.example.com" rules: - backendRefs: - name: store-v1 port: 443 --- kind: TLSRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: store-german spec: parentRefs: - kind: Gateway name: store-tls sectionName: store-german hostnames: - "store-de.example.com" rules: - backendRefs: - name: store-german port: 443השדה
sectionNameבוחר את המאזין המטורגט ב-Gateway. השדהportמציין את יציאת השירות (443), ולא את יציאת הקונטיינר.מחילים את המניפסט של TLSRoute:
kubectl apply -f store-tls-routes.yamlמוודאים שבקר השער קיבל את הנתיב:
kubectl get tlsroute store -o yamlהשדה
statusדומה לשדה הבא:status: parents: - controllerName: networking.gke.io/gateway parentRef: name: store-tls sectionName: store conditions: - type: ResolvedRefs status: "True" reason: ResolvedRefs - type: Accepted status: "True" reason: Accepted - type: Reconciled status: "True" reason: ReconciliationSucceededהתנאי
Reconciledהוא תנאי סטטוס ספציפי ל-GKE, שמדווח אם בקר ה-Gateway יצר את משאבי ה-Google Cloud הבסיסיים.מוודאים שהמאזין של השער קיבל את הנתיב:
kubectl get gateway store-tls -o yamlכל רשומה בשדה
status.listenersדומה לזו:- name: store supportedKinds: - group: gateway.networking.k8s.io kind: TLSRoute attachedRoutes: 1 conditions: - type: ResolvedRefs status: "True" reason: ResolvedRefs - type: Accepted status: "True" reason: Accepted - type: Programmed status: "True" reason: Programmed
אימות ניתוב תעבורת הנתונים
בדיקות התקינות של GKE בקצה עורפי של מעבר TLS מתבצעות על ידי השלמת לחיצת יד בפרוטוקול TLS. יכול להיות שיעברו כמה דקות עד שה-Pods יעברו את בדיקת התקינות הראשונית.
מאחזרים את כתובת ה-IP של השער:
kubectl get gateway store-tls -o jsonpath='{.status.addresses[0].value}'שולחים בקשה לכל שם מארח, ומחליפים את GATEWAY_IP בכתובת ה-IP מהשלב הקודם:
curl --resolve store.example.com:443:GATEWAY_IP \ --cacert store.crt https://store.example.com curl --resolve store-de.example.com:443:GATEWAY_IP \ --cacert store-de.crt https://store-de.example.comכל תשובה מציינת את שם ה-Pod שענה ומהדהדת את שם המארח של ה-SNI שהגיע אליו:
backend: store-v1-6c4b9d8f65-kp8vr sni: store.example.com backend: store-german-7d9f8c5b47-2xqkd sni: store-de.example.comמכיוון שכל בקשה השתמשה בקובץ אחר של רשות אישורים ושתיהן הצליחו, כל קצה עורפי הציג אישור משלו. מאזן העומסים העביר את החיבור המוצפן בלי לסיים אותו.
מוודאים שהניתוב מתבצע במהלך לחיצת היד בפרוטוקול TLS, לפני שהלקוח שולח בקשת HTTP:
openssl s_client -connect GATEWAY_IP:443 \ -servername store-de.example.com </dev/null 2>/dev/null \ | openssl x509 -noout -subjectבפלט מוצג האישור של הקצה העורפי של
store-german:subject=CN=store-de.example.comשינוי רק של הערך של הדגל
-servernameמשנה את האישור שמוחזר, ומאשר שמאזן העומסים בחר את הקצה העורפי על סמך SNI בלבד.שליחת בקשה עם שם מארח SNI שלא תואם לאף מאזין:
curl --resolve other.example.com:443:GATEWAY_IP \ https://other.example.comהחיבור נסגר כי אין מאזין ביציאה
443שתואם לשם המארחother.example.comשל ה-SNI.
הגדרת קיבולת החיבורים לקצה העורפי
בשירותי GKE שמוגדרים במשאבי TLSRoute, בקר השער של GKE מגדיר שירות לקצה העורפי של TCP באמצעות מצב האיזון CONNECTION, ומגדיר את ערך ברירת המחדל של השדה maxConnectionsPerEndpoint ל-100,000,000 (1e8).
מכיוון שבקר ה-Gateway לא יכול לקבוע באופן אוטומטי את מגבלת החיבורים בו-זמנית של קונטיינר, אתם יכולים להשתמש במשאב GCPBackendPolicy כדי לשנות את ערך ברירת המחדל של השדה maxConnectionsPerEndpoint (מ-1 ל-1,000,000,000). כדי לקבוע את מגבלת החיבורים האופטימלית לפודים של ה-backend, צריך לבדוק את הדברים הבאים:
- מגבלות תוכנה: בודקים את הגדרות השרת או האפליקציה, כמו ההוראה
MaxRequestWorkersשל Apache, ההגדרותworker_processes * worker_connectionsשל NGINX, המאפייןmaxConnectionsשל Tomcat או מגבלות מאגר החיבורים של מסד הנתונים. - בדיקת עומסים: מגדילים בהדרגה את החיבורים בו-זמנית ל-Pod מבודד, תוך מעקב אחרי המעבד (CPU), הזיכרון, זמן האחזור והמגבלות של השקע או של תיאור הקובץ.
החלת GCPBackendPolicy בהתאמה אישית
כדי לשנות את ערך ברירת המחדל של השדה maxConnectionsPerEndpoint בשירות לקצה העורפי של TLSRoute, צריך לצרף משאב GCPBackendPolicy עם השדה spec.protocol שמוגדר ל-TCP והשדה balancingMode שמוגדר ל-CONNECTION:
שומרים את המניפסט הבא בתור
backend-policy.yaml:apiVersion: networking.gke.io/v1 kind: GCPBackendPolicy metadata: name: store-v1-backend-policy spec: protocol: TCP default: balancingMode: CONNECTION maxConnectionsPerEndpoint: 1000 targetRef: group: "" kind: Service name: store-v1מחילים את המדיניות:
kubectl apply -f backend-policy.yaml
מידע נוסף על הגדרת מדיניות לקצה העורפי זמין במאמר הגדרת משאבי Gateway באמצעות מדיניות.
הגדרת בדיקות תקינות למעבר TLS
כברירת מחדל, בשירותים לקצה העורפי שמשויכים למשאבי TLSRoute של העברת TLS, בקר השער של GKE מקצה באופן אוטומטי בדיקת תקינות מסוג SSL.
טיפול בהיעדר תוספי SNI מבדיקות
כשמגדירים בדיקות תקינות לקצוות עורפיים של מעבר TLS, צריך להביא בחשבון את האופן שבו בדיקות התקינות של ה-prober פועלות עם נקודות קצה של TLS. Google Cloud
אם האפליקציות בקצה העורפי דורשות שם מארח תקין של SNI כדי להשלים לחיצת יד בפרוטוקול TLS, בדיקות התקינות של SSL שמוגדרות כברירת מחדל ייכשלו. כדי לפתור את הבעיה, אפשר להשתמש באחת מהשיטות הבאות:
- הגדרת אישורי גיבוי: מגדירים את שרת בק-אנד או ה-Pod כך שיציגו אישור TLS כברירת מחדל כשהחיבור ל-TLS מגיע בלי תוסף SNI.
- שימוש בבדיקות תקינות של TCP עם HealthCheckPolicy: מצמידים משאב HealthCheckPolicy (כשהשדה
config.typeמוגדר ל-TCP) לשירות כדי לבדוק את הקישוריות של יציאת ה-TCP הגולמית ולהימנע מלחיצת היד בפרוטוקול TLS.
החלת HealthCheckPolicy בהתאמה אישית
כדי להגדיר פרמטרים של בדיקת תקינות בהתאמה אישית או לעבור לבדיקת תקינות של TCP, צריך להשתמש במשאב HealthCheckPolicy:
שומרים את המניפסט הבא בתור
health-check-policy.yaml:apiVersion: networking.gke.io/v1 kind: HealthCheckPolicy metadata: name: store-health-check spec: default: checkIntervalSec: 10 timeoutSec: 5 healthyThreshold: 2 unhealthyThreshold: 3 config: type: TCP tcpHealthCheck: port: 8443 targetRef: group: "" kind: Service name: store-serviceמחילים את המדיניות:
kubectl apply -f health-check-policy.yaml
מידע נוסף על הגדרת בדיקות תקינות זמין במאמר הגדרת משאבי Gateway באמצעות כללי מדיניות.
הגדרות מתקדמות
בקטע הזה מתוארים דפוסי ניתוב מתקדמים, הגדרות של ריבוי דיירים ופריסות של אשכולות מרובים שנתמכים באמצעות שערים של מעבר TLS ומשאבי TLSRoute.
התאמה של שם מארח עם תווים כלליים לחיפוש
משאבי TLSRoute תומכים בהתאמה של קידומת עם תווים כלליים לחיפוש לשמות מארחים, ופועלים לפי כללי קדימות דטרמיניסטיים כשכמה נתיבים תואמים לאותה תנועה נכנסת.
שומרים את המניפסט הבא כדי להגדיר TLSRoute עם תו כללי לחיפוש:
apiVersion: gateway.networking.k8s.io/v1
kind: TLSRoute
metadata:
name: wildcard-tls-route
spec:
parentRefs:
- name: store-tls
sectionName: store
hostnames:
- "*.example.com"
rules:
- backendRefs:
- name: store-v1
port: 443
כללים לפתרון התנגשויות וסדר עדיפויות
כשכמה משאבי TLSRoute מצורפים לאותו מאזין של Gateway וטוענים לשמות מארחים חופפים או זהים, בקר ה-GKE Gateway פותר את ההתנגשויות באמצעות אותם כללים של ספציפיות של שם מארח וחותמת זמן של יצירה כמו בנתיבי Gateway אחרים:
- התאמות מדויקות של שמות מארחים (כמו
store.example.com) מקבלות עדיפות על פני התאמות של תווים כלליים לחיפוש (כמו*.example.com), והתאמות ארוכות יותר של תווים כלליים לחיפוש מקבלות עדיפות על פני התאמות רחבות יותר של תווים כלליים לחיפוש (כמו*.com). - אם שני משאבי TLSRoute או יותר מצהירים על אותו שם מארח בדיוק, המסלול עם חותמת הזמן הישנה ביותר של היצירה (
metadata.creationTimestamp) מקבל עדיפות. אם חותמות הזמן של היצירה זהות, המזהה הקטן יותר של<namespace>/<name>מבחינה לקסיקוגרפית מכריע את השוויון. בקר השער לא מקבל מסלולים מתנגשים, שמדווחים על מצבAccepted: Falseעם הסיבהConflicted.
מידע נוסף על קדימות של נתיבים זמין במאמר מיזוג, קדימות ואימות של נתיבים.
ניתוב בין מרחבי שמות באמצעות ReferenceGrant
בסביבות עם כמה דיירים, צוות פלטפורמה מרכזי יכול לנהל את השער במרחב שמות של תשתית (כמו gateway-infra), וצוותי אפליקציות יכולים לנהל את המשאבים של TLSRoute ו-Service במרחבי שמות של מפתחים (כמו store-ns).
במאזין של השער, מאפשרים מסלולים מכל מרחבי השמות (או ממרחבי שמות נבחרים):
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: shared-tls-gateway namespace: gateway-infra spec: gatewayClassName: gke-l7-regional-external-managed listeners: - name: tls-passthrough protocol: TLS port: 443 tls: mode: Passthrough allowedRoutes: namespaces: from: All kinds: - kind: TLSRouteבמרחב השמות של האפליקציה (
store-ns), יוצרים משאב ReferenceGrant שמאפשר לשער במרחב השמותgateway-infraלהפנות למשאב TLSRoute או למשאב Service:apiVersion: gateway.networking.k8s.io/v1beta1 kind: ReferenceGrant metadata: name: allow-gateway-infra namespace: store-ns spec: from: - group: gateway.networking.k8s.io kind: Gateway namespace: gateway-infra to: - group: "" kind: Serviceבמרחב השמות של האפליקציה (
store-ns), יוצרים את המשאב TLSRoute שמפנה לשער המשותף:apiVersion: gateway.networking.k8s.io/v1 kind: TLSRoute metadata: name: store-tls-route namespace: store-ns spec: parentRefs: - name: shared-tls-gateway namespace: gateway-infra sectionName: tls-passthrough hostnames: - "store.example.com" rules: - backendRefs: - name: store-service port: 443
שער מעבר TLS עם כמה אשכולות
אתם יכולים לפרוס שערים מרובי אשכולות עם מעבר TLS בכמה אשכולות GKE ב-fleet:
- משתמשים ב-GatewayClass נתמך עם כמה אשכולות (
gke-l7-regional-external-managed-mc,gke-l7-rilb-mcאוgke-l7-cross-regional-internal-managed-mc). - מייצאים את השירות לקצה העורפי לכל ה-fleet באמצעות המשאב ServiceExport.
במשאב TLSRoute, מפנים לקצה העורפי של כמה אשכולות באמצעות משאב ServiceImport:
apiVersion: gateway.networking.k8s.io/v1 kind: TLSRoute metadata: name: mc-store-tls-route spec: parentRefs: - name: mc-tls-gateway sectionName: tls-passthrough hostnames: - "store.example.com" rules: - backendRefs: - group: net.gke.io kind: ServiceImport name: store-service port: 443
כמה כתובות IP בשערים עם כמה אשכולות
כשפורסים שערים עם כמה אשכולות באזורים שונים (למשל כשמשתמשים ב-GatewayClass gke-l7-cross-regional-internal-managed-mc), אפשר להגדיר כמה כתובות IP בשדה spec.addresses כדי לחשוף כתובת VIP פנימית באזורים שונים.
כשמגדירים כמה כתובות IP בשער אחד, בקר השער של GKE מתרגם כל כתובת IP לכלל העברה נפרד. במקביל, הבקר מפנה לשרת proxy משותף לחלוקת העומס ומשתמש שוב באותה הגדרת ניתוב בסיסית. הבקר לא מכפיל את משאבי ה-TLSRoute הבסיסיים במספר כתובות ה-IP, וכך מונע שכפול של מסלולים וניצול של המכסה.
בדוגמה הבאה מוצג שער מרובה אשכולות שמוגדר עם כתובות IP זמניות בשני אזורים:
kind: Gateway
apiVersion: gateway.networking.k8s.io/v1
metadata:
name: internal-cross-region-tls-gateway
spec:
gatewayClassName: gke-l7-cross-regional-internal-managed-mc
addresses:
- type: networking.gke.io/ephemeral-ipv4-address/us-west1
value: "us-west1"
- type: networking.gke.io/ephemeral-ipv4-address/us-east1
value: "us-east1"
listeners:
- name: tls-passthrough
protocol: TLS
port: 443
tls:
mode: Passthrough
allowedRoutes:
kinds:
- kind: TLSRoute
מידע נוסף על פריסת שערים פנימיים בכמה אשכולות זמין במאמר פריסת שער פנימי בכמה אשכולות.
איחוד חיבורי HTTP
לפי RFC 7540 (סעיף 9.1.1), דפדפני אינטרנט ולקוחות HTTP/2 יכולים לעשות שימוש חוזר בחיבור TLS קיים לכמה בקשות, אם מתקיימים שני התנאים הבאים:
- שמות המארחים של היעד מפוענחים לאותה כתובת IP.
- אישור ה-TLS שהשרת מציג הוא סמכותי לשני שמות המארחים.
לדוגמה, אישור יכול לכסות כמה שמות מארחים כשמשתמשים באישורי תו כללי לחיפוש כמו
*.example.comאו בשמות חלופיים של בעלים (SAN) בכמה דומיינים.
בגלל שמאזן עומסים של שרת proxy ל-TCP בשכבה 4 לא בודק את כותרות בקשות ה-HTTP אחרי לחיצת היד הראשונית בפרוטוקול TLS, מאזן העומסים מעביר את הבקשות הממוזגות דרך החיבור הקיים ל-Pod של הקצה העורפי שהותאם בהתחלה.
כדי למנוע בעיות שקשורות לאיחוד חיבורים:
- השתמשו באישורי TLS נפרדים שלא חופפים: אל תשתפו אישורי תווים כלליים לחיפוש או זהויות SAN נפוצות בין שירותים שונים לקצה העורפי, שמועברים בריבוב מאחורי אותה כתובת IP של שער.
- החזרת קוד השגיאה HTTP 421 Misdirected Request: מגדירים את שרתי ה-HTTPS לקצה העורפי כך שיבדקו את הכותרת
:authority(אוHost) של ה-HTTP. אם המארח המבוקש לא תואם לדומיין הצפוי של השרת, מחזירים קוד סטטוסHTTP 421 Misdirected Request, שמבקש מהלקוח לפתוח חיבור חדש. - הפעלת מסגרת HTTP/2 ORIGIN: מאמצים את התמיכה במסגרת RFC 8336 ORIGIN בשרתי המקור ומצהירים במפורש אילו שמות מארחים תקפים לשימוש חוזר בחיבור.
הסרת המשאבים
כדי להימנע מצבירת חיובים בחשבון Google Cloud על המשאבים שבהם השתמשתם בדף הזה, מוחקים את המשאבים:
מוחקים את המשאבים של TLSRoute:
kubectl delete tlsroute store store-germanמוחקים את המשאב Gateway:
kubectl delete gateway store-tlsמוחקים את המשאבים לדוגמה של Deployment, Service ו-Secret:
kubectl delete -f store-tls-app.yaml kubectl delete secret store-cert store-de-cert
המאמרים הבאים
- מידע על היכולות של GatewayClass
- מידע על אבטחת Gateway
- איך מגדירים משאבי Gateway באמצעות כללי מדיניות