🚀

aws-auth ConfigMap Deep Dive

に公開

aws-auth は廃止がアナウンス済みですが、EKS における認証の仕組みを学ぶうえで理解しておくことは有益なので調べた内容をまとめました。

[aws-auth ConfigMap] は廃止されました。Kubernetes API へのアクセスを管理する推奨手段については、「EKS アクセスエントリを使用して Kubernetes へのアクセスを IAM ユーザーに許可する」を参照してください。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/auth-configmap.html

認証の流れ

以下公式ブログ記載の図を参考にしつつ、一部古くなっている箇所があったのでアップデートしました(+若干の加筆)。
https://aws.amazon.com/jp/blogs/containers/kubernetes-rbac-and-iam-integration-in-amazon-eks-using-a-java-based-kubernetes-operator/

この図をもとに以降解説を進めます。

k8s の認証方式

各ステップの説明に入る前に k8s の認証方式について軽く触れます。

k8s では複数の認証方式がサポートされており、EKS では webhook トークン認証を利用しています。webhook トークン認証ではおおまかに以下の流れで認証が行われます。

  1. クライアントから API サーバへ Bearer トークン付きのリクエストを送信
  2. API サーバは認証を別プロセスへ移譲
  3. 別プロセスでの認証が成功すると次のフェース(Authorization)に移る

aws-auth では別プロセスに相当する箇所を aws-iam-authenticator が担っており、ここで aws-auth ConfigMap を利用した認証を行っています。
https://github.com/kubernetes-sigs/aws-iam-authenticator

各ステップの詳細

1. client-go credential plugins と Bearer Token の取得

EKS では kubectl コマンドを実行すると、まず始めに API サーバへのリクエストに使用する Bearer Token を取得します。

KUBECONFIG(k8s の認証ファイル)は以下のようになっており、API サーバにリクエストを送る前に aws eks get-token を実行します。

なお、EKS では aws eks update-kubeconfig --cluster <クラスタ名> で KUBECONFIG を取得できます。

users:
- name: arn:aws:eks:ap-northeast-1:11111111:cluster/test-cluster
  user:
    exec:
      apiVersion: client.authentication.k8s.io/v1beta1
      args:
      - --region
      - ap-northeast-1
      - eks
      - get-token
      - --cluster-name
      - test-cluster
      - --output
      - json
      command: aws
      env: null
      interactiveMode: IfAvailable
      provideClusterInfo: false

これは client-go credential pluginsという機能で k8s.io/client-go がネイティブでサポートしていない認証プロトコルを利用できるようにしています。

.user.exec がある場合は TransportConfig をアップデートしつつ、API サーバへのリクエスト前に exec.command を実行して Bearer Token を設定しています(該当コード)。

ちなみに上記のコマンド(aws eks get-token)を実行すると以下のようなレスポンスが返ってきてます。ここから status.token を取り出して Bearer Token としてセットするようにな処理の流れになっています(該当コード)。

❯ aws eks get-token --cluster-name test-cluster
{
    "kind": "ExecCredential",
    "apiVersion": "client.authentication.k8s.io/v1beta1",
    "spec": {},
    "status": {
        "expirationTimestamp": "2025-01-04T10:47:49Z",
        "token": "<トークンの値>"
    }
}

2. aws-iam-authenticator に認証を委譲

ここからは k8s の control plane で行われる処理になります。

上述の通り EKS では webhook トークン認証を利用しているのでリクエストを受け取った API サーバは認証を aws-iam-authenticator に委譲します。具体的には TokenReview という JSON オブジェクトを作成して、これを POST body に詰めて aws-iam-authenticator にリクエストを送信しています(該当コード)。

TokenReview は以下のような形式になります。

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    "token": "(Bearerトークン)"
  }
}

https://kubernetes.io/ja/docs/reference/access-authn-authz/authentication/#webhook-token-authentication

ちなみに API サーバの起動オプションのログから webhook トークン認証 が利用されていることを確認できます。

❯ aws logs get-log-events --log-group-name /aws/eks/$CLUSTER_NAME/cluster --log-stream-name kube-apiserver-xxxxxxxxx | jq -r '.events[].message' | grep authentication-token-webhook-config-file
I0104 10:59:02.379582      11 flags.go:64] FLAG: --authentication-token-webhook-config-file="/etc/kubernetes/authenticator/apiserver-webhook-kubeconfig.yaml"

3-5. aws-iam-authenticator の認証

次に API サーバ から TokenReview を受け取った aws-iam-authenticator が認証を行います。

まず API サーバから送られてきた Bearer Token を取り出して、そこから prefix(k8s-aws-v1.)を除き、更には base64 デコードして parsedURL としてセットします(該当コード)。

AWS CLI で同等の処理をすると以下のようになります。つまり API サーバから送られてきた Bearer Token から STS へのリクエスト URL を生成しているのです。

❯ aws eks get-token --cluster-name $CLUSTER_NAME \
    | jq -r '.status.token' \
    | sed 's/k8s-aws-v1.//g' \
    | base64 -d
https://sts.ap-northeast-1.amazonaws.com/?Action=GetCallerIdentity&Version=2011-06-15&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...

その後は以下のような流れで認証を完結させます。

  • STS へリクエストを送り STS 側で Barer Token を検証し、結果をレスポンスとして受け取る(該当コード
  • STS のレスポンス等のデータから認証する ID を確定する(該当コード
  • この ID が aws-auth ConfigMap の mapRoles/mapUsers に含まれているかをチェック(該当コード
  • 結果を TokenReview に詰めて API サーバに返却する(該当コード

認証プロセスとしては以上になります。以降は認証した ID に対して Authrization 処理を実行することになります。

6. Access Grant

認証と同じく認可も複数のモードが用意されていますが、aws-auth では RBAC を利用しています。
https://kubernetes.io/docs/reference/access-authn-authz/authorization/#authorization-modules

実際の処理としては以下でやっていて、クライアントから送られたリクエストと RoleBinding/ClusterRoleBinding を照合して、結果(authorizer.Decision)と理由をセットで返却しています。

(具体的にどういう照合しているのかまで踏み込みたかったですが力尽きたのでここまで)

おわりに

aws-auth の仕組みを見ていきましたが、Access Entry も認証までは概ね同じ仕組みで動いている(aws-iam-authenticator を使っている)ので、やる気が出れば詳細調べてみようと思います。

また、記事本文ではコード解説は概略に留めているので、詳細が気になる方は以下スクラップも見てみてください。
https://zenn.dev/ishii1648/scraps/57d000e09b8743

Discussion