RBAC による Kubernetes アクセスのセキュリティ保護

KubernetesBeginner
オンラインで実践に進む

はじめに

Kubernetes の認証は呼び出し元を識別し、認可はそのアイデンティティに許可される操作を決定します。RBAC では、API リソースに対する動詞として権限を表現し、それをユーザー、グループ、または ServiceAccount に割り当てます。

この実験では、名前空間内でのみ利用できる読み取り専用の権限を作成し、そのアイデンティティを権限に結び付けたうえで、インパーソネーションを使って許可される操作と拒否される操作の境界を確認します。

RBAC は、次の 3 つの問いに分けて考えると理解しやすくなります。誰か?(ServiceAccount)、何ができるか?(Role)、誰にその権限を与えるか?(RoleBinding)。これらのオブジェクトを 1 つまたは 2 つ作成するだけでは、認可の経路は完成しません。

ServiceAccount アイデンティティの作成

このステップでは、アプリケーションコンポーネント用の、名前空間にスコープされた ServiceAccount を作成します。

ServiceAccount は、ワークロードや自動化処理で使用する Kubernetes API のアイデンティティです。コンテナ内の Linux ユーザーではありません。この例で専用のアイデンティティを使用することで、名前空間の default ServiceAccount を意図せず継承することを防げます。

cd /home/labex/project/rbac-access
kubectl create serviceaccount inventory-reader -n practice-rbac
kubectl get serviceaccount inventory-reader -n practice-rbac -o yaml

完全なアイデンティティは system:serviceaccount:practice-rbac:inventory-reader です。アイデンティティを作成しただけでは、アプリケーション権限は一切付与されません。

YAML の出力では、metadata によって名前空間と名前を確認できます。長期間有効なトークン Secret が自動的に表示されるとは限りません。現在の Kubernetes では、必要に応じて短期間有効なトークンを Pod に投影します。

返されるオブジェクトには専用の名前と名前空間が含まれますが、自動生成されたトークン Secret は含まれていません。

kind: ServiceAccount
metadata:
  name: inventory-reader
  namespace: practice-rbac

名前空間にスコープされた権限の定義

このステップでは、practice-rbac 内の Pod と ConfigMap の読み取りだけを許可する Role を作成します。

各ルールは、API グループ、リソース、動詞を組み合わせて定義します。Pod や ConfigMap などのコアリソースでは、apiGroups: [""] によって空の文字列を指定します。get、list、watch はそれぞれ、個別の読み取り、コレクションの読み取り、変更ストリームの監視を表します。

cd /home/labex/project/rbac-access
cat <<'EOF' > inventory-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: {name: inventory-reader, namespace: practice-rbac}
rules:
  - apiGroups: [""]
    resources: [pods, configmaps]
    verbs: [get, list, watch]
EOF

Role を適用して内容を表示します。describe はポリシールールを表形式で表示するため、生の YAML よりも確認しやすくなります。

kubectl apply -f inventory-role.yaml
kubectl describe role inventory-reader -n practice-rbac

空の API グループはコアリソースを指定します。Role によって、所属する名前空間の外部へのアクセスが許可されることはありません。

また、指定されていない権限にも注目してください。create、update、patch、delete は含まれていません。最小権限は、付与した権限だけでなく、意図的に付与しなかった権限によっても定義されます。

Resources   Non-Resource URLs  Resource Names  Verbs
---------   -----------------  --------------  -----
configmaps  []                 []              [get list watch]
pods        []                 []              [get list watch]

この表では、YAML でまとめて定義したルールが、リソースごとに 1 行へ展開されています。どちらの行にも、同じ 3 つの読み取り専用の動詞が設定されています。

Role とアイデンティティの関連付け

このステップでは、RoleBinding を使って Role と ServiceAccount を関連付けます。

両方のオブジェクトがすでに存在するため、命令型コマンドで簡潔に作成できます。--role は権限の元となる Role を指定し、--serviceaccount は対象を namespace:name 形式で指定します。

cd /home/labex/project/rbac-access
kubectl create rolebinding inventory-reader -n practice-rbac --role=inventory-reader --serviceaccount=practice-rbac:inventory-reader
kubectl get rolebinding inventory-reader -n practice-rbac -o yaml

roleRef は権限を参照し、subjects はその権限を受け取るアイデンティティを指定します。ここでは、どちらも名前空間の範囲内にあります。

同じ名前の Role と ServiceAccount を作成しても、この RoleBinding が存在するまでは互いに関係ありません。名前は人間が識別するためのものにすぎず、実際の認可の接続を担うのは RoleBinding です。

YAML では、roleRef.name: inventory-reader から、kind が ServiceAccount、name が inventory-reader、namespace が practice-rbac である subject へと、関連付けをたどって確認してください。

許可された操作と拒否された操作のテスト

このステップでは、--as を指定した kubectl auth can-i を使って ServiceAccount になりすまし、権限を確認します。

認可のテストでは、許可されるケースと拒否されるケースの両方を確認する必要があります。yes は必要な操作が可能であることを示し、no は破壊的な操作や別の名前空間へのアクセスが誤って許可されていないことを示します。

cd /home/labex/project/rbac-access
IDENTITY=system:serviceaccount:practice-rbac:inventory-reader
kubectl auth can-i list pods -n practice-rbac --as="$IDENTITY"
kubectl auth can-i get configmaps -n practice-rbac --as="$IDENTITY"
kubectl auth can-i delete pods -n practice-rbac --as="$IDENTITY" || true
kubectl auth can-i list pods -n default --as="$IDENTITY" || true

最初の 2 つの結果は yes、削除と別の名前空間での一覧取得は no になるはずです。インパーソネーションを使うことで、トークンを公開したり作成したりせずに認可をテストできます。

kubectl auth can-i は、結果が no の場合、no という出力とともにゼロ以外のシェルステータスを返します。2 つの || true は、この意図的な拒否テストで処理を継続させるためのものであり、認可の判定自体を変更するものではありません。

yes
yes
no
no

この結果は、必要な 2 つの操作と、保護されている 2 つの境界として読み取れます。yes の結果だけを含むテストでは、最小権限を実証できません。

現在の実験ユーザーには、この ServiceAccount になりすましてテストする権限が与えられています。--as は、そのコマンドで API サーバーが評価するアイデンティティを変更しますが、シェルユーザーを変更したり、kubectl の認証情報を恒久的に切り替えたりするものではありません。

まとめ

ServiceAccount を作成し、Role で名前空間内の読み取り専用権限を定義し、RoleBinding を通じてその権限を割り当てました。さらに、インパーソネーションを使って、許可される操作と拒否される操作の両方を確認しました。