k8s webhook トークン認証の流れ(aws-auth)

k8s バージョン
v1.32.0
API サーバのリクエスト前処理は http.Handler(ミドルウェア)として登録されて実行されている。DefaultBuildHandlerChain()
でまとめて登録されているので認証・認可周りの実装を見る場合はここがエントリになる

起動オプション --authentication-token-webhook-config-file
を指定していると []authenticator.Request に bearertoken.Authenticator が追加される。bearertoken.Authenticator には tokenReviewer が含まれる(tokenReviewer は aws-iam-authenticator へのリクエストで POST body として送られる)。なお、この段階では spec.token
は空
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/pkg/kubeapiserver/authenticator/config.go#L182-L189
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go#L206-L227
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/pkg/kubeapiserver/authenticator/config.go#L191-L198
TokenReview の例
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"spec": {
"token": "(Bearerトークン)"
}
}

[]authenticator.Request に bearertoken.Authenticator が含まれていると AuthenticateRequest() 時に Authorization ヘッダーから Baerer Token を取得して TokenReview.Spec.Token に設定
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go#L67
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/apiserver/pkg/authentication/request/bearertoken/bearertoken.go#L43-L52
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go#L112-L117

token を設定した後 tokenReview を POST Body に設定してリクエスト送信(送信先ってどこで設定してる?)

送信先の設定はここでやってた
--authentication-token-webhook-config-file
で渡すファイルの中身は以下のような形式になっており、ここから clusters[].cluser.server を抜いている
# Kubernetes APIのバージョン
apiVersion: v1
# APIオブジェクトの種類
kind: Config
# clustersは、リモートサービスを指します。
clusters:
- name: name-of-remote-authn-service
cluster:
certificate-authority: /path/to/ca.pem # リモートサービスを検証するためのCA
server: https://authn.example.com/authenticate # クエリするリモートサービスのURL。'https'を使用する必要があります。
# usersは、APIサーバーのWebhook設定を指します。
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # Webhookプラグインを使うための証明書
client-key: /path/to/key.pem # 証明書に合致する鍵
# kubeconfigファイルにはコンテキストが必要です。APIサーバー用のものを用意してください。
current-context: webhook
contexts:
- context:
cluster: name-of-remote-authn-service
user: name-of-api-sever
name: webhook

ちなみに認証は複数実行できるらしい。authenticator.Reques が複数ある場合は unionAuthRequestHandler.Handlers に格納されて順次実行される

ここからは aws-iam-authenticator で認証処理の実装を見ていく

API サーバから送られてきた Bearer Token を取り出す。そこから v1Prefix(k8s-aws-v1.
)を除いた文字列を base64 デコードした値を parsedURL としてセット
- https://github.com/kubernetes-sigs/aws-iam-authenticator/blob/8769c8f063f072830ccd737d29a049a5a4609571/pkg/server/server.go#L326
- https://github.com/kubernetes-sigs/aws-iam-authenticator/blob/8769c8f063f072830ccd737d29a049a5a4609571/pkg/token/token.go#L491-L499
コマンドで同様の処理したが確かに 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=...

parsedURL に GET リクエストを送る(クライアントから送られてきた Bearer Token の有効性を STS で検証)

検証を通過すると Bearer Token と STS のレスポンスを中身を組み合わせて token.Identity として return

token.Identity が ConfigMap に含まれているかを確認しつつ、username と k8s group を取得

Mapper は ConfigMap 以外にもいくつか用意されていて BackendMapper.mappers として管理されていて、どれを使うかはコマンドオプション or 設定ファイル(.server.backendMode
)で指定する
- https://github.com/kubernetes-sigs/aws-iam-authenticator/blob/8769c8f063f072830ccd737d29a049a5a4609571/pkg/server/types.go#L36-L40
- https://github.com/kubernetes-sigs/aws-iam-authenticator/blob/8769c8f063f072830ccd737d29a049a5a4609571/cmd/aws-iam-authenticator/server.go#L108-L111
ちなみに2個以上設定できるようになっていて、その場合は(恐らく)先に指定した mapper が優先されるらしい

最後に TokenReview.Status を埋めて API サーバにレスポンスを返す
成功した場合のレスポンス例
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"status": {
"authenticated": true,
"user": {
"username": "janedoe@example.com",
"uid": "42",
"groups": [
"developers",
"qa"
],
"extra": {
"extrafield1": [
"extravalue1",
"extravalue2"
]
}
}
}
}

以下のように .users[].user.exec
が設定されているケースで kubectl get
を実行して API サーバにリクエストが届くまでの間の処理の流れも見ていく
users:
- name: arn:aws:eks:ap-northeast-1:111111111111111: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

エントリーポイントはここら辺

kubeconfig を load して clientcmdapi.Config
にセットする(ここに .users[].user.exec
も含まれる)
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/kubectl/pkg/cmd/get/get.go#L206
-
https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/cli-runtime/pkg/genericclioptions/config_flags.go#L233
- clientcmd.DeferredLoadingClientConfig を return
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/client-go/tools/clientcmd/merged_client_builder.go#L69
- https://github.com/kubernetes/kubernetes/blob/70d3cc986aa8221cd1dfb1121852688902d3bf53/staging/src/k8s.io/client-go/tools/clientcmd/loader.go#L393-L402

以下で .users[].user.exec
に含まれるコマンドの実行+API サーバへのリクエストしてる
最終的に net/http パッケージの send() がコールされる。send() は RoundTripper.RoundTrip() で実際の HTTP tansaction を実行しているが、RoundTripper は interface になっていて、ここを client-go/rest パッケージで振る舞いを実装してる

ここで exec がある場合は provider.UpdateTransportConfig() して client-go credential plugins の roundTripper を使うよう設定している
ちなみに roundTripper の実装はここ

aws-auth の場合
