このページでは、Google Kubernetes Engine(GKE)クラスタで、安全でない kubelet 読み取り専用ポートを無効にして、kubelet への不正アクセスのリスクを軽減する方法と、より安全なポートにアプリケーションを移行する方法について説明します。
GKE などの Kubernetes クラスタでは、ノードで実行されている 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
を使用します。 - ノード上の Pod のリストなど、ノード上の kubelet API を使用して Kubernetes 情報を取得するワークロードの場合は、代わりに Kubernetes API を使用します。
アプリケーションが安全でない kubelet 読み取り専用ポートを使用しているかどうかを確認する
このセクションでは、クラスタで安全でないポートの使用を確認する方法について説明します。
Autopilot モードでポートの使用状況を確認する
Autopilot クラスタでポートの使用状況を確認するには、クラスタで実行されている DaemonSet 以外のワークロードが少なくとも 1 つあることを確認します。空の 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
このマニフェストの内容は次のとおりです。
- Namespace を作成し、ノードの指標を読み取ることができるように RBAC ロールを設定します。
- 安全でない読み取り専用ポートの kubelet 指標を確認する DaemonSet をデプロイします。
マニフェストをデプロイします。
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 モードでポートの使用状況を確認する
クラスタ内のすべてのノードプールの少なくとも 1 つのノードで、次のコマンドを実行します。
kubectl get --raw /api/v1/nodes/NODE_NAME/proxy/metrics | grep http_requests_total | grep readonly
NODE_NAME
は、ノードの名前で置き換えます。
出力に server_type="readonly"
文字列を含むエントリが含まれている場合は、次のシナリオが考えられます。
- ノードのワークロードが安全でない kubelet 読み取り専用ポートを使用している。
- 安全でないポートを無効にしても、コマンドは
server_type="readonly"
文字列を返します。これは、kubelet_http_requests_total
指標が、kubelet サーバーが最後に再起動してから受信した HTTP リクエストの累積数を表すためです。安全でないポートが無効になっても、この番号はリセットされません。この数値は、GKE が kubelet サーバーを再起動した後(ノード アップグレード中など)にリセットされます。詳細については、Kubernetes 指標リファレンスをご覧ください。
出力が空の場合、対象のノード上のワークロードは安全でない読み取り専用ポートを使用していません。
安全でない kubelet 読み取り専用ポートを使用しているワークロードを特定する
安全でないポートを使用しているワークロードを特定するには、ConfigMap や Pod などのワークロードの構成ファイルを確認します。
次のコマンドを実行します。
kubectl get pods --all-namespaces -o yaml | grep 10255
kubectl get configmaps --all-namespaces -o yaml | grep 10255
コマンドの出力が空でない場合、次のスクリプトを使用して、安全でないポートを使用している ConfigMap または Pod の名前を特定します。
# This function checks if a Kubernetes resource is using the insecure port 10255.
#
# Arguments:
# $1 - Resource type (e.g., pod, configmap, )
# $2 - Resource name
# $3 - Namespace
#
# Output:
# Prints a message indicating whether the resource is using the insecure port.
isUsingInsecurePort() {
resource_type=$1
resource_name=$2
namespace=$3
config=$(kubectl get $resource_type $resource_name -n $namespace -o yaml)
# Check if kubectl output is empty
if [[ -z "$config" ]]; then
echo "No configuration file detected for $resource_type: $resource_name (Namespace: $namespace)"
return
fi
if echo "$config" | grep -q "10255"; then
echo "Warning: The configuration file ($resource_type: $namespace/$resource_name) is using insecure port 10255. It is recommended to migrate to port 10250 for enhanced security."
else
echo "Info: The configuration file ($resource_type: $namespace/$resource_name) is not using insecure port 10255."
fi
}
# Get the list of ConfigMaps with their namespaces
configmaps=$(kubectl get configmaps -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name | tail -n +2 | awk '{print $1"/"$2}')
# Iterate over each ConfigMap
for configmap in $configmaps; do
namespace=$(echo $configmap | cut -d/ -f1)
configmap_name=$(echo $configmap | cut -d/ -f2)
isUsingInsecurePort "configmap" "$configmap_name" "$namespace"
done
# Get the list of Pods with their namespaces
pods=$(kubectl get pods -A -o custom-columns=NAMESPACE:.metadata.namespace,NAME:.metadata.name | tail -n +2 | awk '{print $1"/"$2}')
# Iterate over each Pod
for pod in $pods; do
namespace=$(echo $pod | cut -d/ -f1)
pod_name=$(echo $pod | cut -d/ -f2)
isUsingInsecurePort "pod" "$pod_name" "$namespace"
done
関連するワークロードを特定したら、次のセクションの手順に沿って、セキュアポート 10250 を使用するように移行します。
安全でない kubelet 読み取り専用ポートから移行する
通常、アプリケーションをセキュアポートに移行するには、次の手順を行います。
安全でない読み取り専用ポートを参照する URL またはエンドポイントを更新して、安全な読み取り専用ポートを使用するようにします。たとえば、
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
エンドポイントを使用する場合は、安全な kubelet ポートのエンドポイントにアクセスするための nodes/proxy
RBAC 権限を付与する必要があります。nodes/proxy
は強力な権限であり、GKE Autopilot クラスタでは付与できません。また、GKE Standard クラスタでは付与しないでください。代わりに、ノード名に fieldSelector
を指定して Kubernetes API を使用します。
安全でない kubelet 読み取り専用ポートに依存するサードパーティ アプリケーションを使用している場合は、安全なポート 10250
に移行する手順についてアプリケーション ベンダーに確認してください。
移行の例
安全でない kubelet 読み取り専用ポートから指標をクエリする Pod について考えてみましょう。
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
Namespace でdefault
ServiceAccount を使用します。- ノードの
/metrics
エンドポイントに対してcurl
コマンドを実行します。
セキュアポート 10250
を使用するようにこの Pod を更新する手順は次のとおりです。
ノード指標を取得するアクセス権を持つ ClusterRole を作成します。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: curl-authenticated-role rules: - apiGroups: - "" resources: - nodes/metrics verbs: - get
ClusterRole をアプリケーションの ID にバインドします。
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 クラスタと既存の 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 クラスタで安全でない読み取り専用ポートを無効にする
安全でない kubelet 読み取り専用ポートは、Standard クラスタ全体または個別のノードプールで無効にできます。クラスタ全体でポートを無効にすることをおすすめします。
ノード自動プロビジョニングを使用する場合、自動プロビジョニングされたノードプールは、クラスタレベルで指定されたポート設定を継承します。必要に応じて、自動プロビジョニングされたノードプールに別の設定を指定できますが、クラスタ内のすべてのノードでポートを無効にすることをおすすめします。
ノードシステム構成ファイルを使用して、安全でない 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
: 新しい Standard クラスタの名前。LOCATION
: 新しい Standard クラスタのロケーション。
必要に応じて --no-autoprovisioning-enable-insecure-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 クラスタには、kubelet の読み取り専用ポートのステータスの値を設定する nodeConfig
フィールドも表示されます。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