خلاصه سریع 😊: در نسخه Kubernetes v1.36 قابلیت KubeletFineGrainedAuthz به حالت General Availability (GA) رسیده و feature gate آن قفل شده و همیشه فعال است. این قابلیت دسترسی به API از طریق kubelet را ریزبینی (fine-grained) می‌کند تا به جای اعطای مجوز گسترده nodes/proxy برای ابزارهای مانیتورینگ و observability، اجازه‌ها را با رعایت اصل least‑privilege محدود کنیم.

مشکل قبلی — nodes/proxy

kubelet چندین endpoint حساس دارد: لیست پادها، متریک‌ها، لاگ‌ها و مهم‌تر از همه امکان اجرای فرمان داخل کانتینرها. پیش از این، وقتی webhook authorization فعال بود، تقریباً همه مسیرهای kubelet به یک subresource واحد یعنی nodes/proxy نگاشته می‌شدند؛ بنابراین هر ابزاری که صرفاً می‌خواست متریک یا وضعیت سلامت را بخواند معمولاً به nodes/proxy نیاز داشت — مجوزی که در واقع امکان exec در هر کانتینر روی نود را هم می‌دهد. این رفتار اصل least‑privilege را نقض می‌کرد و شعاع آسیب در صورت compromise ابزارها را به شدت بزرگ می‌کرد.

خطر خاص: WebSocket RCE با nodes/proxy GET ⚠️

مسئله خطرناک‌تر این است که حتی مجوز read-only (مثل nodes/proxy GET) هم قابل سوءاستفاده برای اجرای کد از راه دور شد. دلیل فنی این است که WebSocket handshake از HTTP GET استفاده می‌کند اما kubelet این GET را به RBAC verb برابر با get نگاشت می‌کند و بررسی ثانویه برای وجود CREATE (یا write) انجام نمی‌دهد. بنابراین با یک WebSocket client می‌توان مستقیماً به /exec روی پورت 10250 وصل شد و دستور اجرا کرد. نمونه‌ی حمله با websocat به شکل زیر است:

[pyaml]
websocat –insecure
–header “Authorization: Bearer $TOKEN”
–protocol v4.channel.k8s.io
“wss://$NODE_IP:10250/exec/default/nginx/nginx?output=1&error=1&command=id”
[/yaml]

این ضعف علت اصلی طراحی KEP-2862 و حرکت به سمت احراز هویت ریزبینی kubelet بوده است.

راه‌حل: KubeletFineGrainedAuthz — چگونه کار می‌کند

با فعال شدن KubeletFineGrainedAuthz، kubelet قبل از بازگشت به nodes/proxy یک بررسی دقیق‌تر انجام می‌دهد. چند مسیر پرمصرف الآن به subresourceهای مشخصی نگاشته شده‌اند، مثلاً:

kubelet APIResourceSubresource/stats/* → nodesstats
metrics/* → nodesmetrics
logs/* → nodeslog
/pods → nodespods, proxy/runningPods → nodespods, proxy/healthz → nodeshealthz, proxy/configz → nodesconfigz, proxy/spec/* → nodesspec, checkpoint/* → nodescheckpoint
سایر مسیرها → nodesproxy

برای endpointهایی که الآن subresource مخصوص دارند (مثل /pods، /runningPods، /healthz، /configz) kubelet ابتدا یک SubjectAccessReview برای همان subresource خاص ارسال می‌کند؛ اگر اجازه داده شد، درخواست مجاز شناخته می‌شود و اگر نشد، برای سازگاری به nodes/proxy fallback می‌کند. این مکانیزم dual-check باعث می‌شود مهاجرت نرم و backward‑compatible باشد: workloadهای قدیمی که nodes/proxy دارند همچنان کار کنند و از سوی دیگر امکان تعریف مجوزهای با حداقل دسترسی برای سرویس‌های جدید فراهم شود.

مثال عملی

برای یک Prometheus node exporter که می‌خواهد /metrics را scrape کند، روال قدیمی یک ClusterRole بسیار گسترده بود که nodes/proxy را مجاز می‌دانست:

[pyaml]
# Old approach: overly broad
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-agent
rules:
– apiGroups: [“”]
resources: [“nodes/proxy”]
verbs: [“get”]
[/yaml]

با احراز هویت ریزبینی، می‌توان دقیق‌تر اجازه داد:

[pyaml]
# New approach: least privilege
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: monitoring-agent
rules:
– apiGroups: [“”]
resources: [“nodes/metrics”, “nodes/stats”]
verbs: [“get”]
[/yaml]

در این حالت agent فقط می‌تواند متریک و stats را بخواند و هیچ‌گاه قادر به exec در کانتینرها نخواهد بود — یعنی blast radius در صورت compromise بسیار کمتر می‌شود.

بروزشدن system:kubelet-api-admin

وقتی RBAC فعال باشد، نقش داخلی system:kubelet-api-admin به طور خودکار به‌روزرسانی می‌شود تا دسترسی به تمام subresourceهای جدید را داشته باشد. این کار باعث می‌شود که adminها یا kube-apiserver که قبلاً به این نقش تکیه کرده بودند بدون نیاز به تغییر دستی همچنان کار کنند. نقش اکنون شامل دسترسی به: nodes/proxy، nodes/stats، nodes/metrics، nodes/log، nodes/spec، nodes/checkpoint، nodes/configz، nodes/healthz، nodes/pods است.

ملاحظات هنگام آپگرید

– ارتقا به v1.36 برای اکثر کلاسترها بدون مشکل خواهد بود چون kubelet ابتدا چک ریزبینی را انجام می‌دهد و در صورت نیاز به nodes/proxy برمی‌گردد.
– kube-apiserver همیشه از طریق system:kubelet-api-admin به nodes/proxy دسترسی دارد، پس ارتباط kube-apiserver ↔ kubelet تحت تأثیر قرار نمی‌گیرد.
– کلاسترهای mixed-version هم به خوبی کار می‌کنند چون nodes/proxy به عنوان fallback باقی مانده است.

نحوه بررسی فعال بودن feature

برای تأیید اینکه KubeletFineGrainedAuthz روی یک نود فعال است، می‌توانید از metrics endpoint kubelet استفاده کنید. به‌دلیل نیاز به authorization برای پورت 10250، ابتدا باید RBAC مناسب برای ServiceAccount یا پادی که درخواست می‌فرستد بسازید.

[pyaml]
apiVersion: v1
kind: ServiceAccount
metadata:
name: kubelet-metrics-checker
namespace: default

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kubelet-metrics-reader
rules:
– apiGroups: [“”]
resources: [“nodes/metrics”]
verbs: [“get”]
[/yaml]

[pyaml]
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kubelet-metrics-checker
subjects:
– kind: ServiceAccount
name: kubelet-metrics-checker
namespace: default
roleRef:
kind: ClusterRole
name: kubelet-metrics-reader
apiGroup: rbac.authorization.k8s.io
[/yaml]

[pyaml]
kubectl apply -f serviceaccount.yaml
kubectl apply -f clusterrole.yaml
kubectl apply -f clusterrolebinding.yaml
[/yaml]

[pyaml]
kubectl run kubelet-check
–image=curlimages/curl
–serviceaccount=kubelet-metrics-checker
–restart=Never –rm -it — sh
[/yaml]

از داخل پاد می‌توانید توکن را بخوانید و metrics را query کنید:

[pyaml]
# Get the token
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# Query the kubelet metrics and filter for the feature gate
curl -sk –header “Authorization: Bearer $TOKEN” https://$NODE_IP:10250/metrics | grep kubernetes_feature_enabled | grep KubeletFineGrainedAuthz
[/yaml]

اگر فعال باشد، خروجی شبیه این خواهد بود:

[pyaml]
kubernetes_feature_enabled{name=”KubeletFineGrainedAuthz”,stage=”GA”} 1
[/yaml]

نکته: $NODE_IP را با IP نودی که می‌خواهید بررسی کنید جایگزین کنید. می‌توانید IP نودها را با kubectl get nodes -o wide دریافت کنید.

تاریخچه کوتاه انتشار

– v1.32: معرفی feature gate KubeletFineGrainedAuthz به‌صورت Alpha (غیرفعال پیش‌فرض)
– v1.33: ارتقا به Beta و فعال به‌طور پیش‌فرض؛ بررسی‌های ریزبینی برای /pods، /runningPods، /healthz، /configz اضافه شد
– v1.36: GA؛ feature gate قفل و همیشه فعال شد

قدم بعدی و توصیه‌ها برای SRE/DevOps 👇

– به‌تدریج RBACهای ابزارهای monitoring و observability (مثل Prometheus، Datadog و سایر DaemonSetها) را از nodes/proxy به subresourceهای مشخص (nodes/metrics، nodes/stats، nodes/pods و …) منتقل کنید تا سطح حمله WebSocket RCE حذف شود.
– از admission controller یا policy engines استفاده کنید تا bindingهایی که به‌صورت غیرضروری nodes/proxy می‌دهند شناسایی یا مسدود شوند.
– در محیط‌های تولیدی، نقش‌ها و ClusterRoleهای پرمصرف را بازبینی کنید و به سمت least‑privilege حرکت کنید؛ این تغییرات در بلندمدت خطرات امنیتی را به‌طور قابل‌توجهی کاهش می‌دهند.

اگر می‌خواهید مشارکت کنید

این بهبود توسط SIG Auth و SIG Node پیش برده شد. اگر علاقه‌مند به کمک در بخش authorization و امنیت Kubernetes هستید، به مباحث این SIGها بپیوندید و KEP-2862 را بررسی کنید. 💬🔒