本页面向您展示如何禁用 Google Kubernetes Engine (GKE) 集群中不安全的 kubelet 只读端口,以降低未经授权访问 kubelet 的风险,以及如何将应用迁移到更安全的端口。
在 Kubernetes 集群(包括 GKE)中,节点上运行的 kubelet
进程会使用不安全的端口 10255
提供只读 API。Kubernetes 不会对此端口执行任何身份验证或授权检查。kubelet 会在经过身份验证的更安全端口 10250
上提供相同端点。
停用 kubelet 只读端口,并将使用端口 10255
的所有工作负载切换为更安全的端口 10250
。
准备工作
在开始之前,请确保您已执行以下任务:
- 启用 Google Kubernetes Engine API。 启用 Google Kubernetes Engine API
- 如果您要使用 Google Cloud CLI 执行此任务,请安装并初始化 gcloud CLI。 如果您之前安装了 gcloud CLI,请运行
gcloud components update
以获取最新版本。
使用要求
- 您只能在 GKE 1.26.4-gke.500 版或更高版本中停用不安全的 kubelet 只读端口。
检查是否存在不安全的端口使用情况并迁移应用
在停用不安全的只读端口之前,请将使用该端口的所有正在运行的应用迁移到更安全的只读端口。可能需要迁移的工作负载包括访问 kubelet 端点的自定义指标流水线和工作负载。
- 对于在节点上需要访问 kubelet API 提供的信息(例如指标)的工作负载,请使用端口
10250
。 - 对于在节点上使用 kubelet API 获取 Kubernetes 信息(例如列出节点上的 Pod)的工作负载,请改用 Kubernetes API。
检查应用是否使用不安全的 kubelet 只读端口
本部分介绍如何检查集群中是否存在不安全的端口使用情况。
检查 Autopilot 模式下的端口使用情况
如需检查 Autopilot 集群中的端口使用情况,请确保至少有一个工作负载不是在集群中运行的 DaemonSet。如果您对空的 Autopilot 集群执行以下步骤,则结果可能无效。
将以下清单保存为
read-only-port-metrics.yaml
:apiVersion: v1 kind: Namespace metadata: name: node-metrics-printer-namespace --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: node-metrics-printer-role rules: - apiGroups: - "" resources: - nodes/metrics verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: node-metrics-printer-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: node-metrics-printer-role subjects: - kind: ServiceAccount name: node-metrics-printer-sa namespace: node-metrics-printer-namespace --- apiVersion: v1 kind: ServiceAccount metadata: name: node-metrics-printer-sa namespace: node-metrics-printer-namespace --- apiVersion: apps/v1 kind: DaemonSet metadata: name: node-metrics-printer namespace: node-metrics-printer-namespace spec: selector: matchLabels: app: node-metrics-printer template: metadata: labels: app: node-metrics-printer spec: serviceAccountName: node-metrics-printer-sa containers: - name: metrics-printer image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest command: ["sh", "-c"] args: - 'while true; do curl -s --cacert "${CA_CERT}" -H "Authorization: Bearer $(cat ${TOKEN_FILE})" "https://${NODE_ADDRESS}:10250/metrics"|grep kubelet_http_requests_total; sleep 20; done' env: - name: CA_CERT value: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt - name: TOKEN_FILE value: /var/run/secrets/kubernetes.io/serviceaccount/token - name: NODE_ADDRESS valueFrom: fieldRef: fieldPath: status.hostIP
此清单执行以下操作:
- 创建命名空间并设置 RBAC 角色以允许读取节点指标。
- 部署一个 DaemonSet,用于针对不安全的只读端口检查 kubelet 指标。
部署清单:
kubectl create -f read-only-port-metrics.yaml
检查 DaemonSet 日志:
kubectl logs --namespace=node-metrics-printer-namespace \ --all-containers --prefix \ --selector=app=node-metrics-printer
如果输出有任何结果包含字符串
server_type=readonly
,则表示应用正在使用不安全的只读端口。
检查 Standard 模式下的端口使用情况
在集群的每个节点池中的至少一个节点上运行以下命令:
kubectl get --raw /api/v1/nodes/NODE_NAME/proxy/metrics | grep http_requests_total | grep readonly
将 NODE_NAME
替换为该节点的名称。
如果节点上的工作负载使用不安全的 kubelet 只读端口,则输出会包含具有字符串 server_type="readonly"
的条目,如以下示例所示:
kubelet_http_requests_total{long_running="false",method="GET",path="healthz",server_type="readonly"} 3
kubelet_http_requests_total{long_running="false",method="GET",path="metrics",server_type="readonly"} 2549
kubelet_http_requests_total{long_running="false",method="GET",path="metrics/probes",server_type="readonly"} 2546
kubelet_http_requests_total{long_running="false",method="GET",path="other",server_type="readonly"} 2
kubelet_http_requests_total{long_running="false",method="GET",path="pods",server_type="readonly"} 1
kubelet_http_requests_total{long_running="false",method="GET",path="stats",server_type="readonly"} 2549
如果输出为空,则该节点上没有应用使用不安全的只读端口。
从不安全的 kubelet 只读端口迁移
通常,将应用迁移到安全端口包括以下步骤:
更新引用不安全的只读端口的网址或端点,以改用安全的只读端口。例如,将
http://203.0.113.104:10255
更改为http://203.0.113.104:10250
。将 HTTP 客户端的证书授权机构 (CA) 证书设置为集群 CA 证书。如需查找此证书,请运行以下命令:
gcloud container clusters describe CLUSTER_NAME \ --location=LOCATION \ --format="value(masterAuth.clusterCaCertificate)"
请替换以下内容:
CLUSTER_NAME
:您的集群的名称。LOCATION
:您的集群的位置。
经过身份验证的端口 10250
要求您向主体授予适当的 RBAC 角色,以访问特定资源。如需了解详情,请参阅 Kubernetes 文档中的 kubelet 授权。
/pods
端点,您需要授予 nodes/proxy
RBAC 权限以在安全的 kubelet 端口上访问相应端点。nodes/proxy
是一种强大的权限,您无法在 GKE Autopilot 集群中授予该权限,也不应在 GKE Standard 集群中授予。请改用将 fieldSelector
用于节点名称的 Kubernetes API。
如果您使用依赖于不安全的 kubelet 只读端口的第三方应用,请与应用供应商联系,以获取有关迁移到安全端口 10250
的说明。
示例迁移
假设一个 Pod 从不安全的 kubelet 只读端口查询指标。
apiVersion: v1
kind: Pod
metadata:
name: kubelet-readonly-example
spec:
restartPolicy: Never
containers:
- name: kubelet-readonly-example
image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest
command:
- curl
- http://$(NODE_ADDRESS):10255/metrics
env:
- name: NODE_ADDRESS
valueFrom:
fieldRef:
fieldPath: status.hostIP
此应用会执行以下操作:
- 使用
default
命名空间中的default
ServiceAccount - 在节点上对
/metrics
端点运行curl
命令。
如需更新此 Pod 以使用安全端口 10250
,请执行以下步骤:
创建有权获取节点指标的 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: curl-authenticated-role rules: - apiGroups: - "" resources: - nodes/metrics verbs: - get
将 ClusterRole 绑定到应用的身份:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: curl-authenticated-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: curl-authenticated-role subjects: - kind: ServiceAccount name: default namespace: default
更新
curl
命令以将安全端口端点与相应的授权标头搭配使用:apiVersion: v1 kind: Pod metadata: name: kubelet-authenticated-example spec: restartPolicy: Never containers: - name: kubelet-readonly-example image: us-docker.pkg.dev/cloud-builders/ga/v1/curl:latest env: - name: NODE_ADDRESS valueFrom: fieldRef: fieldPath: status.hostIP command: - sh - -c - 'curl -s --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" https://${NODE_ADDRESS}:10250/metrics'
修改 VPC 防火墙规则
如果您将工作负载更新为使用端口 10250
,请创建防火墙规则,以便集群中的 Pod 可以访问处于节点 IP 地址范围中的该端口。防火墙规则应执行以下操作:
- 允许从内部 Pod IP 地址范围到节点 IP 地址范围中的 TCP 端口
10250
的传入流量 - 拒绝从公共互联网到节点 IP 地址范围中的 TCP 端口
10250
的传入流量。
您可以使用以下默认 GKE 防火墙规则作为模板,以用于要在新规则中指定的参数:
gke-[cluster-name]-[cluster-hash]-inkubelet
gke-[cluster-name]-[cluster-hash]-exkubelet
在 Autopilot 集群上停用不安全的只读端口
您可以为新的和现有 Autopilot 集群停用不安全的 kubelet 只读端口。
在新的 Autopilot 集群上停用不安全的只读端口
如需在创建新的 Autopilot 集群时停用不安全的 kubelet 只读端口,请使用 --no-autoprovisioning-enable-insecure-kubelet-readonly-port
标志,如以下命令所示:
gcloud container clusters create-auto CLUSTER_NAME \
--location=LOCATION \
--no-autoprovisioning-enable-insecure-kubelet-readonly-port
请替换以下内容:
CLUSTER_NAME
:新 Autopilot 集群的名称。LOCATION
:新 Autopilot 集群的位置。
在现有 Autopilot 集群上停用不安全的只读端口
如需在现有 Autopilot 集群上停用不安全的 kubelet 只读端口,请使用 --no-autoprovisioning-enable-insecure-kubelet-readonly-port
标志,如以下命令所示。集群中的所有新节点和现有节点都会停止使用相应端口。
gcloud container clusters update CLUSTER_NAME \
--location=LOCATION \
--no-autoprovisioning-enable-insecure-kubelet-readonly-port
请替换以下内容:
CLUSTER_NAME
:现有集群的名称。LOCATION
:现有集群的位置。
在 Standard 集群上停用不安全的只读端口
您可以为整个 Standard 集群或单个节点池停用不安全的 kubelet 只读端口。我们建议您为整个集群停用相应端口。
如果使用节点自动预配功能,则自动预配的节点池会继承您在集群级别指定的端口设置。您可以选择为自动预配的节点池指定不同的设置,但我们建议您在集群的所有节点上停用相应端口。
您还可以使用节点系统配置文件以声明方式停用不安全的 kubelet 只读端口。如果您使用此文件,则无法使用以下部分中的命令来控制 kubelet 设置。
在新的 Standard 集群上停用不安全的只读端口
如需在新的 Standard 集群上停用不安全的 kubelet 只读端口,请使用 --no-enable-insecure-kubelet-readonly-port
标志,如以下命令所示:
gcloud container clusters create CLUSTER_NAME \
--location=LOCATION \
--no-enable-insecure-kubelet-readonly-port
请替换以下内容:
CLUSTER_NAME
:新标准集群的名称。LOCATION
:新 Standard 集群的位置。
您可以选择添加 --no-autoprovisioning-enable-insure-kubelet-readonly-port
标志以单独控制节点自动预配设置,但我们不建议使用此方法。此标志会对自动预配的节点池启动滚动更新,这可能会导致正在运行的工作负载中断。
在现有 Standard 集群上停用不安全的只读端口
如需在现有 Standard 集群上停用不安全的 kubelet 只读端口,请使用 --no-enable-insecure-kubelet-readonly-port
标志,如以下命令所示。任何新节点池都不会使用不安全的端口。GKE 不会自动更新现有节点池。
gcloud container clusters update CLUSTER_NAME \
--location=LOCATION \
--no-enable-insecure-kubelet-readonly-port
请替换以下内容:
CLUSTER_NAME
:现有 Standard 集群的名称。LOCATION
:现有 Standard 集群的位置。
在 Standard 节点池上停用不安全的只读端口
我们建议您在所有情况下都在集群级别设置只读端口设置。如果您在已具有正在运行的节点池的现有集群上停用了只读端口,请使用以下命令在这些节点池上停用相应端口。
gcloud container node-pools update NODE_POOL_NAME \
--cluster=CLUSTER_NAME \
--location=LOCATION \
--no-enable-insecure-kubelet-readonly-port
请替换以下内容:
NODE_POOL_NAME
:节点池的名称。CLUSTER_NAME
:集群的名称。LOCATION
:集群的位置。
验证端口已停用
如需验证不安全的 kubelet 只读端口是否已停用,请描述 GKE 资源。
检查 Autopilot 集群中的端口状态
运行以下命令:
gcloud container clusters describe CLUSTER_NAME \
--location=LOCATION \
--flatten=nodePoolAutoConfig \
--format="value(nodeKubeletConfig)"
请替换以下内容:
CLUSTER_NAME
:Autopilot 集群的名称。LOCATION
:Autopilot 集群的位置。
如果相应端口已停用,则输出会如下所示:
insecureKubeletReadonlyPortEnabled: false
检查 Standard 集群中的端口状态
当您使用 GKE API 描述集群时,nodePoolDefaults.nodeConfigDefaults.nodeKubeletConfig
字段中会提供端口状态。
在 Standard 集群中,您还会看到 nodeConfig
字段,该字段会设置 kubelet 只读端口状态的值。nodeConfig
字段已弃用,仅适用于在您创建新的 Standard 模式集群时 GKE 所创建的默认节点池。已弃用的 nodeConfig
字段中的端口状态不适用于集群中的其他节点池。
运行以下命令:
gcloud container clusters describe CLUSTER_NAME \
--location=LOCATION \
--flatten=nodePoolDefaults.nodeConfigDefaults \
--format="value(nodeKubeletConfig)"
请替换以下内容:
CLUSTER_NAME
:Standard 集群的名称。LOCATION
:Standard 集群的位置。
如果相应端口已停用,则输出会如下所示:
insecureKubeletReadonlyPortEnabled: false
检查 Standard 节点池中的端口状态
运行以下命令:
gcloud container node-pools describe NODE_POOL_NAME \
--cluster= CLUSTER_NAME \
--location=LOCATION \
--flatten=config \
--format="value(kubeletConfig)"
替换以下内容:
NODE_POOL_NAME
:节点池的名称。CLUSTER_NAME
:集群的名称。LOCATION
:集群的位置。
如果相应端口已停用,则输出会如下所示:
insecureKubeletReadonlyPortEnabled: false