kubernetes

Kubernetes: v1.7 で導入された Node Authorization とは

Kubernetes v1.7 で導入された Node Authorization について概要と kubeadm で構築して試してみた結果をまとめました。確認は Kubernetes v1.7.0 で行っています。

概要

Node Authorization とは Node (Kubelet) の権限管理をより厳密に行う機能です。この機能を有効にすることで、Node はその Node が関連するオブジェクトにのみ権限が制限されるようになります。例えば Node は割り当てられた Pod が参照する Secret 以外にはアクセスできなくなります。これにより Node のクレデンシャルが漏洩したときのセキュリティリスクを最小限にすることができます。

制限されるリソース

Node Authorization を有効にすると Node がアクセスする必要があるリソースをその Node に関連あるものだけに自動的に絞られます。権限は以下のように制御されます。実装上の都合で認可フェーズと Admission Control のフェーズ、の2フェーズで権限の制御が行われています。

  • 認可 (Node Authorizer)による機能
    • その Node に紐づく Pod が参照している Secret, ConfigMap, PersistentVolume, PersistentVolumeClaim のみ参照できる
  • Admission Control (NodeRestriction)による機能
    • その Node に紐づく Node オブジェクトのみを変更できる
    • その Node に紐づく Pod オブジェクトのみステータスを変更できる
    • その Node に紐づく Static な Pod オブジェクト (mirror pod)のみを作成・更新できる
    • mirror pod は Secret などの他のオブジェクトの参照はできないようになっている

その他の Kubelet が参照するリソースに関しては、RBAC の system:node の権限が参考になります。kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go

補足: 実装上の都合

認可フェーズでは、リクエストの body 部分は見ることができず、また Admission Control では読み込みにはかからないという制限があるため、このような 2 つを合わせた実装になっているようです。

参考: https://github.com/kubernetes/community/blob/master/contributors/design-proposals/kubelet-authorizer.md#alternatives-considered

有効にするための設定

Node Authorization を有効にするためには下記の3点の設定が必要になります。

Node 側のクレデンシャル情報

Node のクレデンシャルには以下の情報が含まれている必要があります

  • Group が system:nodes であること
  • User 名が system:node:<nodeName> であること

例えばクライアント証明書による認証を使っている場合は、下記のような Subject になっている必要があります。

Subject: O=system:nodes, CN=system:node:<nodeName>

認可 (Node Authorizer) の設定

API Server の認可の設定にNodeを追加する必要があります。

  • --authorization-mode=Node,...
    • 必要に応じてカンマ区切りで複数かけます。例) Node,RBAC

Admission Control (NodeRestriction) の設定

API Server の Admission Control の設定にNodeRestrictionを追加する必要があります。

  • --admission-control=NodeRestriction,...

kubeadm で Node Authorization を試してみる

kubeadm を使って Node Authorization の機能の効果を試してみます。v1.7.0 の時点で kubeadm のデフォルトで Node Authorization の機能が有効になっているようです。

kubeadm による構築は Using kubeadm to Create a Cluster のドキュメントどおりに行いました。Pod Network には Calico を用いています。

構成

以下のようにMaster 1台、Node 2台の計3台で構成されています。

$ kubectl get nodes
NAME       STATUS    AGE       VERSION
master     Ready     4h        v1.7.0
worker-1   Ready     4h        v1.7.0
worker-2   Ready     4h        v1.7.0

kubeadm によって worker-1, 2 のクライアント証明書の Group 名、Node 名が仕様どおり設定されています。

# worker-1
$ cat /etc/kubernetes/kubelet.conf | grep client-certificate-data | awk '{print $2;}' | base64 -d | openssl x509 -text | grep Subject:
        Subject: O=system:nodes, CN=system:node:worker-1
                   ^^^^^^^^^^^^     ^^^^^^^^^^^^^^^^^^^^
                   Group 名         Node 名
# worker-2
$ cat /etc/kubernetes/kubelet.conf | grep client-certificate-data | awk '{print $2;}' | base64 -d | openssl x509 -text | grep Subject:
        Subject: O=system:nodes, CN=system:node:worker-2

API サーバーの Node Authorization の設定 (--admission-control=...,NodeRestriction,.., --authorization-mode=Node,... の設定)が有効になっていることを確認します。

# master
$ cat /etc/kubernetes/manifests/kube-apiserver.yaml  | grep Node
    - --admission-control=Initializers,NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota
    - --authorization-mode=Node,RBAC

アクセス制御の確認

Node Authorization によって Node に割り当てられた Pod のマウントした Secret のみにアクセスできるよう制御されていることを確認してみます。

まずはアクセス制御の対象となる Secret を作成します。

$ kubectl create secret generic mysecret --from-literal=username=myusername --from-literal=password=mypassword
secret "mysecret" created

次にこの Secret (mysecret)をマウントした Deployment を定義します。

$ kubectl apply -f myapp-dep.yaml 
deployment "myapp" created

Pod がスケジューリングされた Node を確認します。worker-1 に割り当てられたようです。

$ kubectl get pods -o wide
NAME                     READY     STATUS    RESTARTS   AGE       IP               NODE
myapp-1607853247-kmgrp   1/1       Running   0          25s       192.168.226.67   worker-1
                                                                                   ^^^^^^^^

worker-1 のクレデンシャル (/etc/kubernetes/kubelet.conf の接続情報) を使ってこの Secret (mysecret) が読めることを確認します。

# worker-1
$ KUBECONFIG=/etc/kubernetes/kubelet.conf kubectl get secrets mysecret
NAME       TYPE      DATA      AGE
mysecret   Opaque    2         28m
# read できている

worker-1 でPod がマウントしていない他の Secret (othersecret) は読めないことを確認します。

# worker-1
$ KUBECONFIG=/etc/kubernetes/kubelet.conf kubectl get secret othersecret
Error from server (Forbidden): User "system:node:worker-1" cannot get secrets in the namespace "default".: "no path found to object" (get secrets othersecret)
# エラーになった

また Pod が割り当てられていない worker-2 のクレデンシャルを使ってこの Secret が読めないことを確認します。

# worker-2
$ KUBECONFIG=/etc/kubernetes/kubelet.conf kubectl get secrets mysecret
Error from server (Forbidden): User "system:node:worker-2" cannot get secrets in the namespace "default".: "no path found to object" (get secrets mysecret)

Node に関連ある Secret のみにアクセスできるようアクセス制御が正しく動作していることが確認できました :tada:

関連ソースコード

参考