EKSを作ったユーザ、またはロールに紐付く system:masters
というグループがどこから来ているのか、自分の目で確かめたくて掘り進めたので記録を残しておきます。
EKSのsystem:mastersって
以下にあるようにEKSを作ったユーザ、またはロールに紐付くグループです。
When you create an Amazon EKS cluster, the IAM entity user or role, such as a federated user that creates the cluster, is automatically granted system:masters permissions in the cluster's RBAC configuration.
https://docs.aws.amazon.com/eks/latest/userguide/add-user-role.html
この system:masters
はcluster-adminに紐付けられていてスーパーユーザ的に機能します。
以下のように別のロールやユーザにマッピングもできます。(この場合は eks-admin
というIAMロールに管理者権限である system:masters
をマッピングしています)
kubectl apply -f - <<EOF apiVersion: v1 kind: ConfigMap metadata: name: aws-auth namespace: kube-system data: mapRoles: | - rolearn: arn:aws:iam::123456789012:role/eks-admin groups: - system:masters EOF
system:masters
グループ自体は cluster-admin
に紐付いています。これで管理者権限があるのも納得です。
$ kubectl describe clusterrolebindings cluster-admin Name: cluster-admin Labels: kubernetes.io/bootstrapping=rbac-defaults Annotations: rbac.authorization.kubernetes.io/autoupdate: true Role: Kind: ClusterRole Name: cluster-admin Subjects: Kind Name Namespace ---- ---- --------- Group system:masters
ただ、少し調べるとわかるのですが、このGroupというものは、ここで言うClusterRoleのようにK8s上にリソースとして定義されたものではありません。
面白いのは上記の configmap/aws-auth
に system:masters
とクラスタ作成者を紐付ける情報が無いところです。実際にconfigmap/aws-authを削除してしまっても、クラスタを作成したユーザは変わらずに system:masters
に紐付いている(つまりcluster-admin
にも紐付いている)ことが確認できます。
$ kubectl delete -nkube-system configmap/aws-auth configmap "aws-auth" deleted $ kubectl get clusterrole | head # <-変わらず操作ができる NAME AGE admin 14d aws-node 14d 省略
EKSの認証の仕組み
では system:masters
がどこから来たのかを、調べながら追ってみました。
まずEKSの認証の仕組みですが、IAMと深く関わっています。
ユーザ認証を絵の通りIAMに任せています。これはKubernetesが提供している認証(Authentication) の仕組みのうちWebhook Token Authenticationという方式です。並んでいる Static Token File
などと違い動的に権限を設定できる方式となります。
引用元 https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/managing-auth.html
ステップバイステップで追ってみる
このWebhookという方式をキーとして、kubectlからどうやって認証かかっているのか追ってみたいと思います。
kubectlの認証
AWSのドキュメントの手順に則れば、以下のでkubectlの設定ができるのはご存知だと思います。eksctlで作成した場合は自動で設定されてた気もします。
aws eks update-kubeconfig --name yomon8-cluster
ここで設定された内容を見ると以下の通りです。
exec
配下を見るとわかるのですが aws eks get-token
コマンドが呼ばれています。
cat ~/.kube/config # 省略 users: - name: arn:aws:eks:ap-northeast-1:123456789012:cluster/yomon8-cluster user: exec: apiVersion: client.authentication.k8s.io/v1alpha1 args: - --region - ap-northeast-1 - eks - get-token - --cluster-name - yomon8-cluster command: aws
実際に呼んでみます。これはAWSコマンド実行者に紐付くTOKENを取得しています。
aws --region ap-northeast-1 eks get-token --cluster-name yomon8-cluster { "status": { "expirationTimestamp": "2020-11-01T11:20:01Z", "token": "k8s-aws-v1.TOKENTOKENTOKEN" }, "kind": "ExecCredential", "spec": {}, "apiVersion": "client.authentication.k8s.io/v1alpha1" }
実際に、上記の TOKENTOKENTOKEN
のところをBase64でデコードすると以下のような文字列になります。この記事ここまで読んでいる人なら見たことあるような内容になっていると思います。
ちなみに x-k8s-aws-id
とあるとおり、EKS用のものなので、このままcurlに流しても認証できません。
https://sts.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Expires=60&X-Amz-Credential=AKIA....%2Fus-east-1%2Fsts%2Faws4_request&X-Amz-SignedHeaders=host%3Bx-k8s-aws-id&X-Amz-Date=20201101T111553Z&X-Amz-Signature=92598a645f35afe4b70894aee0f487a57afe98449....
ちなみに、以下にあるPythonスクリプトでも同じモノが取得できます。boto3で書かれているので、慣れていれば読みやすいと思います。説明も記載されています。
このトークンをAWSのIDとしてEKSに送り、Webhook認証に流します。
Webhookの認証
次はWebhook部分を見てみます。
Webhookの仕様は以下にも記載があります通り、 TokenReview
というオブジェクトを利用します。
下記のリンクを引用します。
以下のようなリクエストを受けて、
{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "spec": { "token": "(Bearerトークン)" } }
こんな返答が返るようです。 groups
とありますが、ここに system:masters
が入って欲しいです。
{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "status": { "authenticated": true, "user": { "username": "janedoe@example.com", "uid": "42", "groups": [ "developers", "qa" ], "extra": { "extrafield1": [ "extravalue1", "extravalue2" ] } } } }
実行してみる
ここまでの情報を受けて、実際に流してみます。
TokenReview
にリクエストをPOSTする必要があるのですが、その権限は system:auth-delegator
というClusterRoleに付いています。
$ kubectl get clusterrole system:auth-delegator -oyaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: annotations: rbac.authorization.kubernetes.io/autoupdate: "true" creationTimestamp: "2020-10-18T01:14:30Z" labels: kubernetes.io/bootstrapping: rbac-defaults name: system:auth-delegator resourceVersion: "59" selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Aauth-delegator uid: 514a8baf-ea9e-4745-8957-1258839198af rules: - apiGroups: - authentication.k8s.io resources: - tokenreviews verbs: - create - apiGroups: - authorization.k8s.io resources: - subjectaccessreviews verbs: - create
webhook-test-ns
という名前空間をテスト用に作成して、 system:auth-delegator
を付与した webhook-test
というServiceAccountを作成してみます。
kubectl apply -f - << EOF --- apiVersion: v1 kind: Namespace metadata: name: webhook-test-ns --- apiVersion: v1 kind: ServiceAccount metadata: name: webhook-test namespace: webhook-test-ns --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: role-tokenreview-binding namespace: webhook-test-ns roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegator subjects: - kind: ServiceAccount name: webhook-test namespace: webhook-test-ns EOF
Webhook認証を実行するためTokenReview
オブジェクトの権限のある作成したサービスアカウント webhook-test
のトークンを取得します。
$ NS=webhook-test-ns #名前空間を変数に設定 $ kubectl get secrets -n ${NS} NAME TYPE DATA AGE default-token-gvhxs kubernetes.io/service-account-token 3 3h30m webhook-test-token-cm8nl kubernetes.io/service-account-token 3 3h30m $ SA_TOKEN=$(kubectl get secrets -n${NS} webhook-test-token-cm8nl -o jsonpath="{@['data.token']}"|base64 -d) && echo ${SA_TOKEN} # -> ey.... (ServiceAccoutのトークンが出力される)
次にEKSのクラスタを作成したユーザのトークンを取得します。これをWebhook認証にかけます。
$ CLUSTER_NAME=yomon8-cluster $ REGION=ap-northeast-1 $ USER_TOKEN=$(aws --region ${REGION} eks get-token --cluster-name ${CLUSTER_NAME} | jq -r '.status.token') && echo ${USER_TOKEN} # -> k8s-aws-v1.... (クラスタ作成IAMユーザのトークンが出力される。これがsystems:masterに紐付いている)
エンドポイントの情報を取得します。
$ END_POINT=$(aws eks describe-cluster --name ${CLUSTER_NAME} | jq -r '.cluster.endpoint') && echo ${END_POINT} # -> https://xxxxx.ap-northeast-1.eks.amazonaws.com
今まで集めた情報を利用してWebhookにTokenReviewのPOSTを実行します。
$ curl -XPOST --insecure ${END_POINT}/apis/authentication.k8s.io/v1/tokenreviews \ -H "Authorization: Bearer ${USER_TOKEN}" \ -H "Content-Type: application/json" \ -d "{\"apiVersion\": \"authentication.k8s.io/v1\",\"kind\": \"TokenReview\",\"spec\": {\"token\": \"${USER_TOKEN}\"}}"
accessKeyIdにクラスタ作成時のユーザのアクセスキーが入っていました。 system:masters
に紐付いていることが確認できます。
username
も kubernetes-admin
と設定されてますね。
{ "kind": "TokenReview", "apiVersion": "authentication.k8s.io/v1", "metadata": { "creationTimestamp": null }, "spec": { "token": "k8s-aws-v1....." }, "status": { "authenticated": true, "user": { "username": "kubernetes-admin", "uid": "heptio-authenticator-aws:123456789012:AIDxxxxx", "groups": [ "system:masters", "system:authenticated" ], "extra": { "accessKeyId": [ "AKIA......" ] } }, "audiences": [ "https://kubernetes.default.svc" ] } }
ここまで来たら、最後は最初にも書いた以下の定義で system:masters
が cluster-admin
に紐付いていきます。
$ kubectl describe clusterrolebindings cluster-admin Name: cluster-admin Labels: kubernetes.io/bootstrapping=rbac-defaults Annotations: rbac.authorization.kubernetes.io/autoupdate: true Role: Kind: ClusterRole Name: cluster-admin Subjects: Kind Name Namespace ---- ---- --------- Group system:masters
リソースとして定義の無い system:masters
が出てくる場所が見られたので少しスッキリです。
参考
KubernetesのToken Review API - Qiita