はじめに
最近EKSでは「IAM Roles for Service Accounts」が正式にサポートされ、EKSクラスターでこの設定をONにしてPodベースでのIAM roleの割り当てが可能になりました。
kube2iamなどを使ったノード(EC2)ベースのロール割り当ての場合は、AWS SDKが良しなに認証情報を読み込んでくれますが、2019/11/01現在のAWS SDK for Java(ver2)ではPodに割り当てられた認証情報をそのままのコードで読み取ることができませんでした。
その時の対応を含めて、AWS SDKでの認証情報の使用方法をまとめます。
IAM Roles for Service Accountsとは
その名の通り、KubernetesリソースであるService AccountにIAM Roleを割り当てる仕組みです。
Kubernetes 1.3以降を実行するEKSクラスターで有効にすることができます。
AWSのブログ記事がとても分かりやすいので、そちらを参照していただければと思います。
この機能を有効化したEKSクラスターはOpenID Connectの認可サーバーのエンドポイントが発行され、Pod及びAWS SDKはこの認可サーバーにトークンを検証してもらいIAM Roleの割り当てを受けます。
Podに割り当てられる情報
(具体的な記述方法については省略しますが、)アノテーションを利用してIAM Roles for Service Accountsを使用したPodには、以下の情報が割り当てられます。
kubectl describe pods
等で確認できると思います。
- 環境変数
AWS_ROLE_ARN
arn:aws:iam::000000000000:role/xxxxxxxxx-role
- 環境変数
AWS_WEB_IDENTITY_TOKEN_FILE
/var/run/secrets/eks.amazonaws.com/serviceaccount/token
- OIDC用のサービスアカウントファイルのボリュームマウント
/var/run/secrets/eks.amazonaws.com/serviceaccount
これらの情報を使いEKSクラスターのOIDCエンドポイントからroleの認証情報の引き受けをAWS SDKは行う必要があります。
AWS SDK for Java(ver2)
上述したOIDCエンドポイントから認証情報を取得する実装は、古いAWS SDKではサポートされておらず、対応しているバージョンまで引き上げる必要があります。
公式のドキュメントを参照するとAWS SDK for Java(ver2)は2.7.36以上が必要であるようなのでバージョンを上げます。
- val awsSdkVersion = "2.7.23"
+ val awsSdkVersion = "2.10.4"
dependencies {
implementation(platform("software.amazon.awssdk:bom:$awsSdkVersion"))
implementation("software.amazon.awssdk:s3")
}
WebIdentityTokenFileCredentialsProviderというクラスがIAM Roles for Service Accountでの認証方式のProviderのようです。
環境変数 AWS_ROLE_ARN
や AWS_WEB_IDENTITY_TOKEN_FILE
、またそれに対応するJavaのシステムプロパティを読んで処理をしているみたいです。
ドキュメントを参照する限り対応はこれだけで良いのかと思ったのですが、以下の二点の対応をしないと動作しませんでした。
Security Token Serviceモジュールの追加
AWS SDKがIAM Roleを引き受けるときに、Security Token Serviceの実装が必要になります。
val awsSdkVersion = "2.10.4"
dependencies {
implementation(platform("software.amazon.awssdk:bom:$awsSdkVersion"))
implementation("software.amazon.awssdk:s3")
+ implementation("software.amazon.awssdk:sts")
}
実装を見てみるとわかりますが、 Class.forName()
しているので、モジュールが不足していてもコンパイルエラーにはならず実行時エラーになる点に注意が必要です。
https://github.com/aws/aws-sdk-java-v2/blob/f3265c681e68dbbadca9d8794617a067dda9959d/core/auth/src/main/java/software/amazon/awssdk/auth/credentials/internal/WebIdentityCredentialsUtils.java#L43-L55
credentialsProviderの明示的な指定
Issueにも挙がっていますが、2019/11/01現在の最新バージョン( 2.10.4
)でも、デフォルトの認証情報プロバイダチェーンに上述のWebIdentityTokenFileCredentialsProviderは含まれておらず、Service AccountのIAM roleを引き受けるには明示的にcredentialsProviderを指定する必要があります。
(Issueをぱっと見た感じAWS SDK for Java(ver2)以外でもこの問題はあるようです)
single {
S3AsyncClient.builder()
+ .credentialsProvider(WebIdentityTokenFileCredentialsProvider.create())
.endpointOverride(URI(s3Config.endpoint))
.region(Region.of(s3Config.region))
.build()
}
ローカル環境
credentialsProviderを明示的に指定した場合デフォルトの認証情報取得の処理は行われないため、ローカル環境での処理が動かなくなってしまいました。
仕方がないのでとりあえず以下の形で対応しました。
時間があればこの辺りを読んでPRでも出せたら良いな、と思っています。
single {
if (!System.getenv("AWS_ROLE_ARN").isNullOrEmpty()) {
S3AsyncClient.builder()
.credentialsProvider(WebIdentityTokenFileCredentialsProvider.create())
.endpointOverride(URI(s3Config.endpoint))
.region(Region.of(s3Config.region))
.build()
} else {
S3AsyncClient.builder()
.endpointOverride(URI(s3Config.endpoint))
.region(Region.of(s3Config.region))
.build()
}
}
まとめ
- AWS SDK for Java(ver2)では2019/11/01現在最新バージョンに上げるだけではIAM Roles for Service Accountsの認証情報を引き受けることができない
- stsの依存の追加と、credentialsProviderの明示的な指定が必要
- 他のSDKでも同様の実装になっているかもしれない(未確認)
- デフォルトの認証情報プロバイダチェーンへの追加は、Issueにもあるので今後対応されると思われる