הרשאות שמוגדרות בצורה שגויה או חסרות בחשבונות שירות של Google Kubernetes Engine (GKE) עלולות לגרום לבעיות שונות, כמו כשל ברישום של צמתים או חוסר אפשרות של עומסי עבודה לגשת לשירותים של Google Cloud .
אפשר להשתמש במסמך הזה כדי לפתור בעיות שנגרמות בגלל חשבונות שירות שהוגדרו בצורה שגויה, הושבתו או נמחקו.
המידע הזה חשוב לאדמינים ולמפעילים של הפלטפורמה, ולמהנדסי אבטחה שמגדירים ומנהלים הרשאות IAM ברמת הפרויקט עבור צמתי GKE ורכיבי ליבה של GKE. מידע נוסף על התפקידים הנפוצים ומשימות לדוגמה שאנחנו מתייחסים אליהם בתוכן של Google Cloud זמין במאמר תפקידי משתמשים נפוצים ומשימות ב-GKE.
הקצאת התפקיד הנדרש ל-GKE לחשבונות שירות של צמתים
באשכולות GKE שפועלת בהם גרסה 1.33 של Kubernetes או גרסה מוקדמת יותר, לחשבונות השירות של IAM שבהם משתמשים צמתי GKE צריכות להיות כל ההרשאות שכלולות בתפקיד IAM Kubernetes Engine Default Node Service Account (roles/container.defaultNodeServiceAccount). אם חסרה לחשבון שירות של צומת GKE הרשאה אחת או יותר מההרשאות האלה, מערכת GKE לא יכולה לבצע משימות מערכת כמו:
- שליחת יומני מערכת ויומני אפליקציות מצמתים אל Cloud Logging.
- שליחת מדדים של מערכת ואפליקציות מצמתים אל Cloud Monitoring.
- הפעלה של פרופיל הביצועים של Horizontal Pod Autoscaler.
יכול להיות שלחשבונות שירות של צמתים אין הרשאות נדרשות מסוימות, בגלל סיבות כמו:
- הארגון אוכף את
iam.automaticIamGrantsForDefaultServiceAccountsהאילוץ של מדיניות הארגון, שמונע מ- Google Cloud להעניק באופן אוטומטי תפקידי IAM לחשבונות שירות של IAM שמוגדרים כברירת מחדל. - תפקיד ה-IAM שאתם מקצים לחשבונות שירות של צמתים בהתאמה אישית לא כולל את כל ההרשאות הנדרשות שכלולות בתפקיד
roles/container.defaultNodeServiceAccount.
אם לחשבון השירות של הצומת חסרות ההרשאות ש-GKE דורש, יכול להיות שיוצגו שגיאות והודעות כמו אלה:
- במסוף Google Cloud , בדף Kubernetes clusters, מוצגת הודעת השגיאה Grant critical permissions בעמודה Notifications עבור אשכול ספציפי.
במסוף Google Cloud , בדף פרטי האשכול של אשכול ספציפי, מופיעה הודעת השגיאה הבאה:
Grant roles/container.defaultNodeServiceAccount role to Node service account to allow for non-degraded operations.ביומני הביקורת של Cloud, יומני הפעילות של האדמין עבור Google Cloud ממשקי API כמו
monitoring.googleapis.comכוללים את הערכים הבאים אם ההרשאות המתאימות לגישה לממשקי ה-API האלה חסרות בחשבון השירות של הצומת:- מידת החוּמרה:
ERROR - הודעה:
Permission denied (or the resource may not exist)
- מידת החוּמרה:
יומנים של צמתים ספציפיים חסרים ב-Cloud Logging, וביומני ה-Pod של סוכן הרישום בצמתים האלה מופיעות
401שגיאות. כדי לקבל את היומנים של ה-Pod, מריצים את הפקודה הבאה:[[ $(kubectl logs -l k8s-app=fluentbit-gke -n kube-system -c fluentbit-gke | grep -cw "Received 401") -gt 0 ]] && echo "true" || echo "false"אם הפלט הוא
true, אז יש401שגיאות בעומס העבודה של המערכת, שמעידות על חוסר הרשאות.
כדי לפתור את הבעיה, צריך להעניק את התפקיד Kubernetes Engine Default Node Service Account (roles/container.defaultNodeServiceAccount) בפרויקט לחשבון השירות שגורם לשגיאות. בוחרים באחת מהאפשרויות הבאות:
console
כדי למצוא את השם של חשבון השירות שבו הצמתים משתמשים:
עוברים לדף Kubernetes clusters:
ברשימת האשכולות, לוחצים על שם האשכול שרוצים לבדוק.
מוצאים את השם של חשבון השירות של הצומת. תצטרכו את השם הזה בהמשך.
- בקטע Security של אשכולות במצב Autopilot, מוצאים את השדה חשבון שירות.
- לגבי אשכולות במצב רגיל:
- לוחצים על הכרטיסייה Nodes.
- בטבלה Node pools (מאגרי צמתים), לוחצים על שם של מאגר צמתים. הדף פרטי מאגר הצמתים נפתח.
- בקטע Security, מוצאים את השדה חשבון שירות.
אם הערך בשדה חשבון שירות הוא
default, הצמתים משתמשים בחשבון השירות שמוגדר כברירת מחדל של Compute Engine. אם הערך בשדה הזה לאdefault, הצמתים משתמשים בחשבון שירות בהתאמה אישית.
כדי להעניק לחשבון השירות את התפקיד Kubernetes Engine Default Node Service Account:
נכנסים לדף Welcome:
בשדה מספר הפרויקט, לוחצים על העתקה ללוח.
נכנסים לדף IAM:
לוחצים על Grant access.
בשדה New principals, מציינים את השם של חשבון השירות של הצומת. אם הצמתים משתמשים בחשבון השירות שמוגדר כברירת מחדל של Compute Engine, צריך לציין את הערך הבא:
PROJECT_NUMBER-compute@developer.gserviceaccount.comמחליפים את
PROJECT_NUMBERבמספר הפרויקט שהעתקתם.בתפריט Select a role בוחרים בתפקיד Kubernetes Engine Default Node Service Account.
לוחצים על Save.
כדי לוודא שהתפקיד הוקצה, מבצעים את הפעולות הבאות:
- בדף IAM, לוחצים על הכרטיסייה View by roles (תצוגה לפי תפקידים).
- מרחיבים את הקטע Kubernetes Engine Default Node Service Account (חשבון השירות של צומת ברירת המחדל של Kubernetes Engine). מוצגת רשימה של חשבונות משתמשים שיש להם את התפקיד הזה.
- מחפשים את חשבון השירות של הצומת ברשימת החשבונות הראשיים.
gcloud
מוצאים את השם של חשבון השירות שבו הצמתים משתמשים:
- עבור אשכולות במצב Autopilot, מריצים את הפקודה הבאה:
gcloud container clusters describe CLUSTER_NAME \ --location=LOCATION \ --flatten=autoscaling.autoprovisioningNodePoolDefaults.serviceAccount- לגבי אשכולות במצב רגיל, מריצים את הפקודה הבאה:
gcloud container clusters describe CLUSTER_NAME \ --location=LOCATION \ --format="table(nodePools.name,nodePools.config.serviceAccount)"אם הפלט הוא
default, הצמתים משתמשים בחשבון השירות שמוגדר כברירת מחדל של Compute Engine. אם הפלט לאdefault, הצמתים משתמשים בחשבון שירות בהתאמה אישית.איך מוצאים את Google Cloud מספר הפרויקט:
gcloud projects describe PROJECT_ID \ --format="value(projectNumber)"מחליפים את
PROJECT_IDבמזהה הפרויקט.הפלט אמור להיראות כך:
12345678901מקצים לחשבון השירות את התפקיד
roles/container.defaultNodeServiceAccount:gcloud projects add-iam-policy-binding PROJECT_ID \ --member="SERVICE_ACCOUNT_NAME" \ --role="roles/container.defaultNodeServiceAccount"מחליפים את
SERVICE_ACCOUNT_NAMEבשם של חשבון השירות שמצאתם בשלב הקודם. אם הצמתים משתמשים בחשבון השירות שמוגדר כברירת מחדל של Compute Engine, צריך לציין את הערך הבא:serviceAccount:PROJECT_NUMBER-compute@developer.gserviceaccount.comמחליפים את
PROJECT_NUMBERבמספר הפרויקט מהשלב הקודם.מוודאים שהתפקיד הוענק בהצלחה:
gcloud projects get-iam-policy PROJECT_ID \ --flatten="bindings[].members" --filter=bindings.role:roles/container.defaultNodeServiceAccount \ --format='value(bindings.members)'הפלט הוא השם של חשבון השירות.
זיהוי אשכולות שבהם לחשבונות השירות של הצמתים חסרות הרשאות
אפשר להשתמש בהמלצות GKE של NODE_SA_MISSING_PERMISSIONS
סוג המשנה של הכלי להמלצות כדי לזהות אשכולות Autopilot ו-Standard שיש בהם חשבונות שירות של צמתים עם הרשאות חסרות. שירות המלצות מזהה רק אשכולות שנוצרו ב-1 בינואר 2024 או אחריו. כדי למצוא ולתקן את ההרשאות החסרות באמצעות Recommender, פועלים לפי השלבים הבאים:
אפשר למצוא המלצות פעילות בפרויקט עבור סוג המשנה של שירות ההמלצות
NODE_SA_MISSING_PERMISSIONS:gcloud recommender recommendations list \ --recommender=google.container.DiagnosisRecommender \ --location LOCATION \ --project PROJECT_ID \ --format yaml \ --filter="recommenderSubtype:NODE_SA_MISSING_PERMISSIONS"מחליפים את מה שכתוב בשדות הבאים:
-
LOCATION: המיקום שבו רוצים למצוא המלצות. -
PROJECT_ID: מזהה הפרויקט ב- Google Cloud .
הפלט אמור להיראות כך, ולהצביע על כך שלאשכול יש חשבון שירות של צומת עם הרשאות חסרות:
associatedInsights: # lines omitted for clarity recommenderSubtype: NODE_SA_MISSING_PERMISSIONS stateInfo: state: ACTIVE targetResources: - //container.googleapis.com/projects/12345678901/locations/us-central1/clusters/cluster-1יכול להיות שיחלפו עד 24 שעות עד שההמלצה תופיע. הוראות מפורטות זמינות במאמר בנושא צפייה בתובנות ובהמלצות.
-
לכל אשכול שמופיע בפלט של השלב הקודם, מחפשים את חשבונות השירות של הצמתים שמשויכים אליו ומקצים לחשבונות השירות האלה את התפקיד הנדרש. לפרטים, אפשר לעיין בהוראות שבקטע הענקת התפקיד הנדרש לחשבונות שירות של צמתים ב-GKE.
אחרי שמעניקים את התפקיד הנדרש לחשבונות השירות של הצומת שזוהו, יכול להיות שההמלצה תמשיך להופיע למשך עד 24 שעות, אלא אם תסגרו אותה באופן ידני.
זיהוי כל חשבונות השירות של הצמתים שחסרות להם הרשאות
אפשר להריץ סקריפט שמחפש מאגרי צמתים באשכולות Standard ו-Autopilot של הפרויקט, כדי למצוא חשבונות שירות של צמתים שאין להם את ההרשאות הנדרשות ל-GKE. הסקריפט הזה משתמש ב-CLI של gcloud ובכלי jq. כדי לראות את הסקריפט, מרחיבים את הקטע הבא:
הצגת התסריט
#!/bin/bash
# Set your project ID
project_id=PROJECT_ID
project_number=$(gcloud projects describe "$project_id" --format="value(projectNumber)")
declare -a all_service_accounts
declare -a sa_missing_permissions
# Function to check if a service account has a specific permission
# $1: project_id
# $2: service_account
# $3: permission
service_account_has_permission() {
local project_id="$1"
local service_account="$2"
local permission="$3"
local roles=$(gcloud projects get-iam-policy "$project_id" \
--flatten="bindings[].members" \
--format="table[no-heading](bindings.role)" \
--filter="bindings.members:\"$service_account\"")
for role in $roles; do
if role_has_permission "$role" "$permission"; then
echo "Yes" # Has permission
return
fi
done
echo "No" # Does not have permission
}
# Function to check if a role has the specific permission
# $1: role
# $2: permission
role_has_permission() {
local role="$1"
local permission="$2"
gcloud iam roles describe "$role" --format="json" | \
jq -r ".includedPermissions" | \
grep -q "$permission"
}
# Function to add $1 into the service account array all_service_accounts
# $1: service account
add_service_account() {
local service_account="$1"
all_service_accounts+=( ${service_account} )
}
# Function to add service accounts into the global array all_service_accounts for a Standard GKE cluster
# $1: project_id
# $2: location
# $3: cluster_name
add_service_accounts_for_standard() {
local project_id="$1"
local cluster_location="$2"
local cluster_name="$3"
while read nodepool; do
nodepool_name=$(echo "$nodepool" | awk '{print $1}')
if [[ "$nodepool_name" == "" ]]; then
# skip the empty line which is from running `gcloud container node-pools list` in GCP console
continue
fi
while read nodepool_details; do
service_account=$(echo "$nodepool_details" | awk '{print $1}')
if [[ "$service_account" == "default" ]]; then
service_account="${project_number}-compute@developer.gserviceaccount.com"
fi
if [[ -n "$service_account" ]]; then
printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" $service_account $project_id $cluster_name $cluster_location $nodepool_name
add_service_account "${service_account}"
else
echo "cannot find service account for node pool $project_id\t$cluster_name\t$cluster_location\t$nodepool_details"
fi
done <<< "$(gcloud container node-pools describe "$nodepool_name" --cluster "$cluster_name" --zone "$cluster_location" --project "$project_id" --format="table[no-heading](config.serviceAccount)")"
done <<< "$(gcloud container node-pools list --cluster "$cluster_name" --zone "$cluster_location" --project "$project_id" --format="table[no-heading](name)")"
}
# Function to add service accounts into the global array all_service_accounts for an Autopilot GKE cluster
# Autopilot cluster only has one node service account.
# $1: project_id
# $2: location
# $3: cluster_name
add_service_account_for_autopilot(){
local project_id="$1"
local cluster_location="$2"
local cluster_name="$3"
while read service_account; do
if [[ "$service_account" == "default" ]]; then
service_account="${project_number}-compute@developer.gserviceaccount.com"
fi
if [[ -n "$service_account" ]]; then
printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" $service_account $project_id $cluster_name $cluster_location $nodepool_name
add_service_account "${service_account}"
else
echo "cannot find service account" for cluster "$project_id\t$cluster_name\t$cluster_location\t"
fi
done <<< "$(gcloud container clusters describe "$cluster_name" --location "$cluster_location" --project "$project_id" --format="table[no-heading](autoscaling.autoprovisioningNodePoolDefaults.serviceAccount)")"
}
# Function to check whether the cluster is an Autopilot cluster or not
# $1: project_id
# $2: location
# $3: cluster_name
is_autopilot_cluster() {
local project_id="$1"
local cluster_location="$2"
local cluster_name="$3"
autopilot=$(gcloud container clusters describe "$cluster_name" --location "$cluster_location" --format="table[no-heading](autopilot.enabled)")
echo "$autopilot"
}
echo "--- 1. List all service accounts in all GKE node pools"
printf "%-60s| %-40s| %-40s| %-10s| %-20s\n" "service_account" "project_id" "cluster_name" "cluster_location" "nodepool_name"
while read cluster; do
cluster_name=$(echo "$cluster" | awk '{print $1}')
cluster_location=$(echo "$cluster" | awk '{print $2}')
# how to find a cluster is a Standard cluster or an Autopilot cluster
autopilot=$(is_autopilot_cluster "$project_id" "$cluster_location" "$cluster_name")
if [[ "$autopilot" == "True" ]]; then
add_service_account_for_autopilot "$project_id" "$cluster_location" "$cluster_name"
else
add_service_accounts_for_standard "$project_id" "$cluster_location" "$cluster_name"
fi
done <<< "$(gcloud container clusters list --project "$project_id" --format="value(name,location)")"
echo "--- 2. Check if service accounts have permissions"
unique_service_accounts=($(echo "${all_service_accounts[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
echo "Service accounts: ${unique_service_accounts[@]}"
printf "%-60s| %-40s| %-40s| %-20s\n" "service_account" "has_logging_permission" "has_monitoring_permission" "has_performance_hpa_metric_write_permission"
for sa in "${unique_service_accounts[@]}"; do
logging_permission=$(service_account_has_permission "$project_id" "$sa" "logging.logEntries.create")
time_series_create_permission=$(service_account_has_permission "$project_id" "$sa" "monitoring.timeSeries.create")
metric_descriptors_create_permission=$(service_account_has_permission "$project_id" "$sa" "monitoring.metricDescriptors.create")
if [[ "$time_series_create_permission" == "No" || "$metric_descriptors_create_permission" == "No" ]]; then
monitoring_permission="No"
else
monitoring_permission="Yes"
fi
performance_hpa_metric_write_permission=$(service_account_has_permission "$project_id" "$sa" "autoscaling.sites.writeMetrics")
printf "%-60s| %-40s| %-40s| %-20s\n" $sa $logging_permission $monitoring_permission $performance_hpa_metric_write_permission
if [[ "$logging_permission" == "No" || "$monitoring_permission" == "No" || "$performance_hpa_metric_write_permission" == "No" ]]; then
sa_missing_permissions+=( ${sa} )
fi
done
echo "--- 3. List all service accounts that don't have the above permissions"
if [[ "${#sa_missing_permissions[@]}" -gt 0 ]]; then
printf "Grant roles/container.defaultNodeServiceAccount to the following service accounts: %s\n" "${sa_missing_permissions[@]}"
else
echo "All service accounts have the above permissions"
fiהסקריפט הזה חל על כל אשכולות GKE בפרויקט.
אחרי שמזהים את השמות של חשבונות השירות שחסרות להם הרשאות, מקצים להם את התפקיד הנדרש. לפרטים, אפשר לעיין בהוראות שבקטע הענקת התפקיד הנדרש לחשבונות שירות של צמתים ב-GKE.
שחזור חשבון השירות שמוגדר כברירת מחדל בפרויקט Google Cloud
יכול להיות שחשבון השירות שמוגדר כברירת מחדל ב-GKE, container-engine-robot, יבוטל בטעות מהפרויקט. תפקיד סוכן שירות של Kubernetes Engine (roles/container.serviceAgent) הוא תפקיד בניהול זהויות והרשאות גישה (IAM) שמעניק לחשבון השירות את ההרשאות לניהול משאבי האשכול. אם מסירים את הקישור הזה של התפקיד מחשבון השירות, חשבון השירות שמוגדר כברירת מחדל לא מקושר יותר לפרויקט, וזה יכול למנוע ממכם לפרוס אפליקציות ולבצע פעולות אחרות באשכול.
כדי לבדוק אם חשבון השירות הוסר מהפרויקט, אפשר להשתמש במסוף Google Cloud או ב-Google Cloud CLI.
המסוף
במסוף Google Cloud , נכנסים לדף IAM & Admin.
gcloud
מריצים את הפקודה הבאה:
gcloud projects get-iam-policy PROJECT_IDמחליפים את
PROJECT_IDבמזהה הפרויקט.
אם container-engine-robot לא מופיע בלוח הבקרה או בפקודה בין חשבונות השירות, התפקיד לא משויך לחשבון.
כדי לשחזר את הקישור של תפקיד סוכן השירות של Kubernetes Engine (roles/container.serviceAgent), מריצים את הפקודות הבאות:
PROJECT_NUMBER=$(gcloud projects describe "PROJECT_ID" \
--format 'get(projectNumber)') \
gcloud projects add-iam-policy-binding PROJECT_ID \
--member "serviceAccount:service-${PROJECT_NUMBER}@container-engine-robot.iam.gserviceaccount.com" \
--role roles/container.serviceAgent
מוודאים ששחזור הקישור של התפקיד בוצע:
gcloud projects get-iam-policy PROJECT_ID
אם שם חשבון השירות מופיע לצד התפקיד container.serviceAgent, קישור התפקיד ישוחזר. לדוגמה:
- members:
- serviceAccount:service-1234567890@container-engine-robot.iam.gserviceaccount.com
role: roles/container.serviceAgent
הפעלת חשבון השירות של Compute Engine שמוגדר כברירת מחדל
חשבון השירות שמשמש את מאגר הצמתים הוא בדרך כלל חשבון השירות שמוגדר כברירת מחדל ב-Compute Engine. אם חשבון השירות שמוגדר כברירת מחדל מושבת, יכול להיות שהצמתים לא יצליחו להירשם באשכול.
כדי לבדוק אם חשבון השירות מושבת בפרויקט, אפשר להשתמש במסוףGoogle Cloud או ב-CLI של gcloud.
המסוף
במסוף Google Cloud , נכנסים לדף IAM & Admin.
gcloud
- מריצים את הפקודה הבאה:
gcloud iam service-accounts list --filter="NAME~'compute' AND disabled=true"
אם חשבון השירות מושבת, מריצים את הפקודות הבאות כדי להפעיל אותו:
איך מוצאים את Google Cloud מספר הפרויקט:
gcloud projects describe PROJECT_ID \ --format="value(projectNumber)"מחליפים את
PROJECT_IDבמזהה הפרויקט.הפלט אמור להיראות כך:
12345678901מפעילים את חשבון השירות:
gcloud iam service-accounts enable PROJECT_NUMBER-compute@developer.gserviceaccount.comמחליפים את
PROJECT_NUMBERבמספר הפרויקט מהפלט של השלב הקודם.
מידע נוסף זמין במאמר פתרון בעיות ברישום צמתים.
שגיאה 400 או 403: חסרות הרשאות עריכה בחשבון
אם חשבון השירות נמחק, יכול להיות שתופיע שגיאה לגבי הרשאות עריכה חסרות. כאן מוסבר איך לפתור את השגיאה הזו.
המאמרים הבאים
אם לא מצאתם פתרון לבעיה שלכם במסמכים, תוכלו להיעזר בקבלת תמיכה, כולל עצות בנושאים הבאים:
- פתיחת בקשת תמיכה באמצעות פנייה אל Cloud Customer Care.
- קבלת תמיכה מהקהילה על ידי פרסום שאלות ב-StackOverflow ושימוש בתג
google-kubernetes-engineכדי לחפש בעיות דומות. אפשר גם להצטרף לערוץ Slack#kubernetes-engineכדי לקבל תמיכה נוספת מהקהילה. - פתיחת דיווחים על בעיות או בקשות להוספת תכונות באמצעות הכלי הציבורי למעקב אחר בעיות.