0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【初学者向け】Kubernetes の過度な権限を持つ RBAC 設定を理解する

Last updated at Posted at 2024-10-19

はじめに

Kubenetesを触り始めて一週間、セキュリティ的に考慮しなきゃなところって何あるかなって探してたら、OWASP Kubernetes Top 10なるものを発見。その中のK03: Overly Permissive RBACを試してたら意外なところでつまづいたりして、同じ思いしてほしくないので公開しました。

Kubenetesにおいてロールベースのアクセス制御(RBAC)は、誰がどのリソースやアクションにアクセスできるかを制御するために不可欠。意図せずユーザーやサービスアカウントに不要な WATCH 権限を与えられていると悪用できちゃうよってハナシ。

このチュートリアルでは、Minikube クラスタをセットアップして、実際にミスったRBAC設定で、コマンドコピペで動作するところまで書きました。


目次

環境と準備

Minikube のセットアップ

まず、Minikube を使用してローカル Kubernetes クラスタをセットアップ。

ステップ 1: Minikube を起動

Docker ドライバーを使用して Minikube を起動。今回はDocker使ったけど、何でも良いと思う。なんのドライバも設定しないと、Qemuが使われてエラー出た。

minikube start --driver=docker

ステップ 2: Minikube のステータスを確認

Minikube が正常に稼働していることを確認:

minikube status

今回のミソ

Kubernetes では、WATCH 権限によりユーザーはリソースの変更を監視できる。リソースが変更されると、WATCH 権限によりリソース全体のオブジェクトが見られるようになる。

WATCH 権限を持つサービスアカウントの作成

default ネームスペース内で、 WATCH 権限のみを持つサービスアカウントを作成する。

ステップ 1: サービスアカウントを作成

kubectl create serviceaccount only-watch-secrets-sa -n default

ステップ 2: WATCH 権限を持つ Role を作成

only-watch-secrets-role.yaml というファイルを作成し、以下の内容を記述:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: only-watch-secrets-role
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["watch"]

Role を適用:

kubectl apply -f only-watch-secrets-role.yaml

ステップ 3: Role をサービスアカウントにバインド

only-watch-secrets-rolebinding.yaml というファイルを作成し、以下の内容を記述:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: default
  name: only-watch-secrets-binding
subjects:
- kind: ServiceAccount
  name: only-watch-secrets-sa
  namespace: default
roleRef:
  kind: Role
  name: only-watch-secrets-role
  apiGroup: rbac.authorization.k8s.io

RoleBinding を適用:

kubectl apply -f only-watch-secrets-rolebinding.yaml

不正なアクセスの実演

このサービスアカウントを使用してシークレットの変更を監視し、機密データがどのように露出するかを確認する。

ステップ 1: サービスアカウントのトークンを取得

Kubernetes v1.24 以降の場合:

TOKEN=$(kubectl -n default create token only-watch-secrets-sa)

Kubernetes v1.24 未満の場合:

SECRET_NAME=$(kubectl -n default get sa only-watch-secrets-sa -o jsonpath='{.secrets[0].name}')
TOKEN=$(kubectl -n default get secret "$SECRET_NAME" -o jsonpath='{.data.token}' | base64 --decode)

トークンが空でないことを確認する:

echo $TOKEN

ステップ 2: API サーバーのアドレスを取得

APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')

ステップ 3: シークレットの GET を試みる(失敗するはず)

サービスアカウントのトークンを使用してシークレットを取得:

curl -k -X GET "$APISERVER/api/v1/namespaces/default/secrets/my-secret" \
  -H "Authorization: Bearer $TOKEN"

期待される出力

{
  "kind": "Status",
  "apiVersion": "v1",
  "status": "Failure",
  "message": "secrets \"my-secret\" is forbidden: User \"system:serviceaccount:default:only-watch-secrets-sa\" cannot get resource \"secrets\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "code": 403
}

ステップ 4: シークレットの監視を開始

kubectl proxy による予期せぬ挙動

当初、kubectl proxy を使用して以下のコマンドを実行:

kubectl proxy &

その後、curl コマンドでシークレットにアクセス:

curl http://127.0.0.1:8001/api/v1/namespaces/default/secrets/my-secret \
  -H "Authorization: Bearer $TOKEN"

ここでつまずいた!シークレットの内容が表示された。

問題点

  • kubectl proxy を使用すると、リクエストはプロキシを起動したユーザーの権限で処理される(らしい)。
  • Authorization ヘッダーは無視される

問題の解決と正しいアプローチ

kubectl proxy を使用せず、API サーバーに直接リクエストを送信する。クエリパラメータをwatch=trueにしてる。

curl -k -X GET "$APISERVER/api/v1/namespaces/default/secrets?watch=true" \
  -H "Authorization: Bearer $TOKEN"

ステップ 5: 別のターミナルで新しいシークレットを作成

新しいターミナルを開き、シークレットを作成する:

kubectl create secret generic my-secret --from-literal=secretPassword=verySecure

ステップ 6: 出力を確認

curl コマンドを実行しているターミナルで以下のような出力が表示される:

{
  "type": "ADDED",
  "object": {
    "kind": "Secret",
    "apiVersion": "v1",
    "metadata": {
      "name": "my-secret",
      "namespace": "default",
      ...
    },
    "data": {
      "secretPassword": "dmVyeVNlY3VyZQ=="
    },
    "type": "Opaque"
  }
}

ステップ 8: シークレットデータをデコード

Base64 エンコードされたシークレットをデコードする:

echo 'dmVyeVNlY3VyZQ==' | base64 --decode

出力

verySecure

説明GETLIST 権限がなくても、WATCH 権限によりサービスアカウントがシークレットデータにアクセスできた!!

クリーンアップ

このチュートリアルで作成したリソースを削除する。

# 監視コマンドを停止(実行中の場合)
# curl の監視コマンドを実行しているターミナルで Ctrl+C を押します

# シークレットを削除
kubectl delete secret my-secret

# RoleBinding を削除
kubectl delete rolebinding only-watch-secrets-binding -n default

# Role を削除
kubectl delete role only-watch-secrets-role -n default

# サービスアカウントを削除
kubectl delete serviceaccount only-watch-secrets-sa -n default

# 必要に応じて、Minikube を停止
minikube stop

結論

WATCH 権限の扱いには気をつけることが一番重要って結論付けたかったけど、kubectl proxy の挙動が一番勉強になった。


レッツKubernetes!!!!!!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?