EKSでのAWSユーザorロール認証の流れを追う
記事一覧はこちら
要約
下記の記事で、k8s内のリソースからAWSのリソースを操作する時の権限のマッピングについて書いたけど、今回はIAMロールorユーザがk8sのリソースを操作する時の権限のマッピングについての話です。
k8sのwebhook認証を利用して、EKSはaws-iam-authentificator
とやり取りしてIAMの情報からk8sのsubjectを取得しています。そのフローの中で利用されるのがkube-system/aws-auth
のConfigMap。k8sのRoleBindingで定義されるユーザやグループにIAMロール/ユーザをマッピングできます。
EKSにおけるAuthentification(認証)
参考にさせていただいたのは下記の記事です。
全体の流れは、Kubernetes RBAC and IAM Integration in Amazon EKS using a Java-based Kubernetes Operator | Containers」に記載されている図がとてもわかり易いので、そちらを引用させていただきます。
起点となるのはkubectl
となります。もし直接k8s APIを叩くとしても流れとして大きくは変わらないのでkubectl
で話を勧めていきます。
kubectl
には、client-go credential pluginsという機能があります。k8s.io/client-go
がネイティブにサポートしていない認証プロトコルとクライアントを統合してくれるものです。サービスアカウントトークン等とは違い、IAMでの認証はネイティブにサポートされていないので、このプラグインを利用することになります。
EKSの場合に用いるプラグインはaws-iam-authenticatorとなります。kubeconfig
には下記のように宣言します。
# [...] users: - name: ops user: exec: apiVersion: client.authentication.k8s.io/v1alpha1 command: aws-iam-authenticator args: - "token" - "-i" - "<CLUSTER_ID>" - "-r" - "<ROLE_ARN>" # no client certificate/key needed here!
aws-iam-authenticator token -i <CLUSTER_ID> -r <ROLE_ARN>
が実行され、標準出力に下記のように出力します。これがBearer Token
として、Authorization
HTTPヘッダとして付与され、k8sのAPIサーバにリクエストされます。このトークンの中身は署名付きURLをbase64エンコードされたものになっています(該当コード)。URLを署名するためにはもちろん認証情報が必要になります。ARNを指定した場合には、そのロールにAssumeRoleを行うことになります。
この辺の流れは、Pythonのコードの方が分かりやすいかも。
ここまでが冒頭で引用させていただいた図の①より前の部分になります。
{ "apiVersion": "client.authentication.k8s.io/v1beta1", "kind": "ExecCredential", "status": { "token": "my-bearer-token", "expirationTimestamp": "2018-03-05T17:30:20-08:00" } }
Bearer Token
の付与されたリクエストはk8sのAPIサーバに到達します。APIサーバではそのトークンが正しいかどうかを検証する必要があります。IAMの認証情報であるため、k8sだけでは検証することは不可能です。k8sはBearer Token
を検証するためにWebhook認証という機能があります。別のコンポーネントにPOSTリクエストを投げて、下記のようなレスポンスを返却してもらうことで、k8sでのユーザ/グループを特定します。
{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "status": { "authenticated": true, "user": { "username": "janedoe@example.com", "uid": "42", "groups": [ "developers", "qa" ], "extra": { "extrafield1": [ "extravalue1", "extravalue2" ] } } }
トークンを検証する別のコンポーネントというのがaws-iam-authenticator
のサーバです。aws-iam-authenticator server
のサブコマンドで起動します。EKSの(おそらく)マスターノードで動いています。k8sのAPIサーバはこのエンドポイントをどう知りうるのかというと、aws-iam-authenticator
サーバ起動時に生成される設定ファイルを--authentication-token-webhook-config-file
の引数に渡してあげることで認識できます。
冒頭で引用した①に当たる部分で、k8sのAPIサーバからトークンがaws-iam-authenticator
のエンドポイント/authenticate
にPOSTリクエストで渡されます。②に当たる部分でトークンの有効性を確認します(該当コード)。その後は、③に当たる部分で、ユーザ名とグループ名をaws-auth
というConfigMapと照らし合わせてAPIサーバに返す(該当コード)。
k8sのAPIサーバは渡されたユーザ名とグループ名に基づいて、Authorization(認可)を行うという流れになります。
aws-authの定義の仕方
ほとんどこのドキュメントに書いてあること。
IAMロール or IAMユーザをKubernetesのユーザにマッピングするための定義
mapRoles: - rolearn: arn:aws:iam::xxxxxxxxxxx:role/admin username: admin # k8sが扱うときの名前になる(※1)。 groups: - system:node # RoleBindingで指定する、subjectのkindがGroupのものと対応する mapUsers: - userarn: arn:aws:iam::xxxxxxxxxxx:user/ops username: ops # subjectのkindがUserのものと名称が一致すれば、その権限を得る(※2)。opsというユーザが既に存在。 --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: ops roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: edit subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: ops
※1: エラーメッセージとかもError from server (Forbidden): services is forbidden: User "admin" cannot list resource "services" in API group "" in the namespace "default"
のように表示される