セッショントークンを利用して新しめのリージョンにAPIリクエストする際は、STSの設定に注意する


概要

 あるシステムで、AssumeRoleによって取得した一時トークンを使ってバーレーンリージョンのリソースに対してGETリクエストを送ると、認証周りでエラーとなったため調べてみると、STSの設定を変更する必要があるとわかった。その際に調べたことや検証したことをまとめる。

STS エンドポイントの種類によってトークンの有効範囲が違う

 AssumeRole等によって一時セッショントークンを発行する際はSTS用のエンドポイントを経由する。そのエンドポイントは大きくわけて以下の2種類ある

  1. グローバルエンドポイント
    • https://sts.amazonaws.com の単一のエンドポイント
  2. リージョンエンドポイント
    • https://sts.{region名}.amazonaws.com の形のエンドポイント
    • リージョンごとに存在し、こちらを使うことが推奨されている。

 どちらを選んでも同じようにセッショントークンを取得することができるが、新しめのリージョンに対するリクエスト時の認証を行う場合は注意が必要。

 リージョンエンドポイントを経由して取得したセッショントークンは、全てのリージョンに対して認証が有効なトークンとなっている。

 ただし、グローバエンドポイントを経由して取得したセッショントークンは、デフォルトでは、新しめのリージョン (=2019年3月20日以降に設けられたリージョンで、明示的に有効化が必要となるもの) に対するリクエストは無効となっており、以下のリージョン (現時点のもの) に対するリクエストは認証エラーとなる。

  • アフリカ (ケープタウン)
  • アジアパシフィック (香港)
  • ヨーロッパ (ミラノ)
  • 中東 (バーレーン)

参考: https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande-manage.html#rande-manage-enable

 デフォルトではこのような設定となっているが、グローバルエンドポイントを使う場合でも全てのリージョンで有効となるトークンを発行できるよう設定ができる。

デフォルト値の場合

設定確認

 設定の状態は、IAMのコンソール画面の「アクセス管理>アカウント設定>Security Token Service (STS)」の「STS エンドポイントからのセッショントークン」で確認可能。

 以下のように、エンドポイントごとにどのリージョン範囲で有効なのかが表示されており、図のようにグローバルエンドポイントで「デフォルトで有効になっているAWSリージョンでのみ有効」となっている。

APIリクエストしてみる

 この状態で、グローバルエンドポイント経由でセッショントークンを取得して、バーレーンリージョンのEC2インスタンスのリストを取得してみる。

AssumeRoleでセッショントークンの取得

 regionを指定して実行すると、そのリージョンのリージョンエンドポイントが利用される。
 region未指定または --region aws-global をつけて実行すると、グローバルエンドポイントが利用される (※ 環境変数やconfig等にリージョンが指定されていた場合はそのリージョンとなる)

aws sts assume-role --role-arn arn:aws:iam::xxxxxxxxxxxx:role/my-role --role-session-name testsession --region  aws-global

 どのエンドポイントが利用されたのかは、--debug フラグをつけることでログに表示されるため、それで確認ができる。

セッショントークンを取得後は、必要な認証情報を環境変数に設定しておく
参考: https://aws.amazon.com/jp/premiumsupport/knowledge-center/iam-assume-role-cli/

バーレーンリージョンのEC2インスタンスのリストを取得

セッショントークンの取得および環境変数への設定後、バーレーンリージョンのEC2インスタンスのリストを取得してみる。

aws ec2 describe-instances --region me-south-1

An error occurred (AuthFailure) when calling the DescribeInstances operation: AWS was not able to validate the provided access credentials

出力内容の通り、AuthFailure というように認証でエラーとなる。

どのようなエラーになるのかはAPIによって異なり、例えば UnrecognizedClientException となるものもあった。

設定の変更方法

「編集」から変更可能。

「すべての AWS リージョンで有効」を選択し「変更の保存」をする。

保存後、以下のように、グローバルエンドポイントについても互換性が「すべてのAWSリージョンで有効」となる。

参考: https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html

全てのリージョンで有効化されている状態でリクエスト

上と同じAssumeRoleのコマンドを使って再度一時トークンを取得し、同じAPIを実行。
そのままのトークンでは、有効範囲が前の設定に沿ったもののままとなる。

aws ec2 describe-instances --region me-south-1
{
    "Reservations": [
        {
            "Groups": [],
            "Instances": [
                {

....

エラーも出ず、バーレーンリージョンのインスタンスリストが取得できた。

切り替えの影響

反映速度は体感は即時だった。

また影響についてはドキュメントでは、

この設定を変更すると、一時的にトークンを保存する既存のシステムに影響する可能性があります。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html#sts-regions-manage-tokens

となっているので、慎重にする場合は検証用の別環境で試してから切り替えることを推奨する。

まとめ

 今回もそうだったが、AssumeRoleして複数リージョンに対して何かしらリクエストを送るようなシステムで発生しうる症状。
 リージョンエンドポイントの利用が推奨となっているため、今後はリージョンを指定してAssumeRoleをするようにしたいが、意図的に or 恣意的にグローバルエンドポイントが使われていることも少なくない (そもそもIAMがグローバルリソース) と思うため、もしその時に新しめのリージョンに対してのリクエストで認証周りのエラーが出た場合は、とりあえずこの対応で解消できる