AWS CloudShell 上でフェデレーションサインイン用 URL を生成してみた
ちょっと環境を覗いてもらうために一時的な URL を払い出す
コンバンハ、千葉(幸)です。
「ちょっと環境を覗いてもらうために AWS マネジメントコンソールへの接続情報を提供したい」
「かといってわざわざ専用の IAM ユーザーやロールを払い出すのは避けたい」
ということがあるかもしれません。
AWS CloudShell のドキュメントを眺めていると、そんなケースにマッチしそうなチュートリアルを見つけました。
フェデレーションサインイン用の URL を生成するスクリプトを載せてくれているので、これが活用できそうです。
ここでは特定の S3 バケットの特定のプレフィックス配下のオブジェクトに対する操作のみ許可するというシチュエーションが想定されていますが、ある程度柔軟にカスタマイズできるのでそれ以外のケースでも転用できます。
何をするのか
やっていることは以下のエントリとほぼ同じです。ここではローカルの端末で bash スクリプトを用いて実現していましたが、AWS CloudShell 上で Python で実行する、というだけの違いです。
AWS CloudShell では実行に必要な環境が揃っていますので、ローカルを汚さずにささっと実行できるのがちょっと嬉しいところです。
上記のエントリと同じ図を用いて説明すると、全体の流れは以下です。
- AssumeRole による一時的な認証情報(アクセスキー/シークレットアクセスキー/セッショントークン)を取得する
- 認証情報を含んだリクエストを AWS フェデレーションエンドポイントに実行し、サインイントークンを取得する
- サインイントークンを含む URL を生成し、コンソールアクセスを行う
ここでは IAM ユーザーが AssumeRole を実行していますが、今回は AWS CloudShell に接続している IAM ユーザー/IAM ロールセッションプリンシパルが実行者になります。
やってみた
やっていきます。
AssumeRole する先のロールを準備
最初のステップで AssumeRole する先のロールを準備しておきます。
最終的にフェデレーションサインインするフェデレーテッドユーザーは、このロールを引き受けたセッションプリンシパルとして扱われます。必要な権限のみを割り当てておくようにしましょう。
AWS CloudShell を操作する IAM エンティティからの AssumeRole を信頼ポリシーで許可しておく必要があります。
今回はcm-chiba.yukihiro
という IAM ロールにスイッチロールして AWS CloudShell に接続するので、AssumeRole 先のロールの信頼ポリシーは以下内容にしました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::000000000000:role/cm-chiba.yukihiro" }, "Action": "sts:AssumeRole", "Condition": { "Bool": { "aws:MultiFactorAuthPresent": "true" } } } ] }
ロールの名称はTest-RoleA
にしておきます。
AWS CloudShell への接続
cm-chiba.yukihiro で AWS CloudShell に接続します。プリンシパルを確認するとこのような感じ。
$ aws sts get-caller-identity { "UserId": "AROAQ3BIIH732QEGJXBGU:cm-chiba.yukihiro", "Account": "000000000000", "Arn": "arn:aws:sts::000000000000:assumed-role/cm-chiba.yukihiro/cm-chiba.yukihiro" }
せっかくなので Python 関連の各種情報を確認しておきます。
バージョン。
$ python3 --version Python 3.7.10
ライブラリの一覧。今回のスクリプトで必要となるものは揃っています。
$ pip3 list Package Version --------------------- --------- arrow 1.2.3 attrs 22.1.0 aws-lambda-builders 1.19.0 aws-sam-cli 1.59.0 aws-sam-translator 1.52.0 awscli 1.19.93 awsebcli 3.20.3 backports.zoneinfo 0.2.1 bcrypt 4.0.1 binaryornot 0.4.4 blessed 1.19.1 boto3 1.24.89 botocore 1.20.93 cached-property 1.5.2 cement 2.8.2 certifi 2022.9.24 cffi 1.15.1 chardet 4.0.0 chevron 0.14.0 click 7.1.2 colorama 0.4.3 cookiecutter 2.1.1 cryptography 38.0.1 dateparser 1.1.1 docker 4.2.2 docker-compose 1.25.5 dockerpty 0.4.1 docopt 0.6.2 Flask 1.1.4 future 0.16.0 git-remote-codecommit 1.16 idna 2.10 importlib-metadata 5.0.0 itsdangerous 1.1.0 Jinja2 2.11.3 jinja2-time 0.2.0 jmespath 0.10.0 jsonschema 3.2.0 MarkupSafe 2.0.1 paramiko 2.11.0 pathspec 0.9.0 pip 20.2.2 pyasn1 0.4.8 pycparser 2.21 PyNaCl 1.5.0 pyrsistent 0.18.1 python-dateutil 2.8.2 python-slugify 6.1.2 pytz 2022.4 PyYAML 5.4.1 regex 2021.9.30 requests 2.25.1 rsa 4.7.2 s3transfer 0.4.2 semantic-version 2.8.5 serverlessrepo 0.1.10 setuptools 49.1.3 six 1.14.0 termcolor 1.1.0 text-unidecode 1.3 texttable 1.6.4 tomlkit 0.7.2 typing-extensions 3.10.0.0 tzlocal 3.0 urllib3 1.26.12 watchdog 2.1.2 wcwidth 0.1.9 websocket-client 0.59.0 Werkzeug 1.0.1 wheel 0.37.1 zipp 3.9.0
ついでなので気になるライブラリの詳細を確認。
$ pip3 show boto3 botocore requests Name: boto3 Version: 1.24.89 Summary: The AWS SDK for Python Home-page: https://github.com/boto/boto3 Author: Amazon Web Services Author-email: None License: Apache License 2.0 Location: /usr/local/lib/python3.7/site-packages Requires: botocore, jmespath, s3transfer Required-by: serverlessrepo, aws-sam-translator, aws-sam-cli --- Name: botocore Version: 1.20.93 Summary: Low-level, data-driven core of boto 3. Home-page: https://github.com/boto/botocore Author: Amazon Web Services Author-email: None License: Apache License 2.0 Location: /home/cloudshell-user/.local/lib/python3.7/site-packages Requires: jmespath, python-dateutil, urllib3 Required-by: s3transfer, awscli, git-remote-codecommit, boto3, awsebcli --- Name: requests Version: 2.25.1 Summary: Python HTTP for Humans. Home-page: https://requests.readthedocs.io Author: Kenneth Reitz Author-email: me@kennethreitz.org License: Apache 2.0 Location: /usr/local/lib/python3.7/site-packages Requires: idna, urllib3, chardet, certifi Required-by: docker, docker-compose, cookiecutter, awsebcli, aws-sam-cli
Python スクリプトの実行
準備が揃ったところでスクリプトを実行していきます。今回は以下内容のスクリプトをshare.py
という名称で作成しました。
import urllib, json, sys import requests import boto3 def main(): sts_client = boto3.client('sts') assume_role_response = sts_client.assume_role( RoleArn="arn:aws:iam::000000000000:role/Test-RoleA", RoleSessionName="Test-Session" ) credentials = assume_role_response['Credentials'] url_credentials = {} url_credentials['sessionId'] = credentials.get('AccessKeyId') url_credentials['sessionKey'] = credentials.get('SecretAccessKey') url_credentials['sessionToken'] = credentials.get('SessionToken') json_string_with_temp_credentials = json.dumps(url_credentials) print(f"json string {json_string_with_temp_credentials}") request_parameters = f"?Action=getSigninToken&Session={urllib.parse.quote(json_string_with_temp_credentials)}" request_url = "https://signin.aws.amazon.com/federation" + request_parameters r = requests.get(request_url) signin_token = json.loads(r.text) request_parameters = "?Action=login" request_parameters += "&Issuer=Example.org" request_parameters += "&Destination=" + urllib.parse.quote("https://us-west-2.console.aws.amazon.com/cloudshell") request_parameters += "&SigninToken=" + signin_token["SigninToken"] request_url = "https://signin.aws.amazon.com/federation" + request_parameters # Send final URL to stdout print (request_url) if __name__ == "__main__": main()
基本的にはドキュメントに記載のものをそのまま踏襲していますが、引き受けるロールを指定する箇所はベタ書きに変えています。(ドキュメントの例では環境変数に設定していた。)
また、25 行目でフェデレーションサインした後の遷移先の URL をコントロールできます。今だとオレゴンリージョンの CloudShell に接続されるようになっています。コンソールのホームに遷移させたければ`https://console.aws.amazon.com/`に変更しましょう。
スクリプトを実行するとまずは AssumeRole により取得した一時クレデンシャルの情報が表示され、その後にフェデレーションサイン用 URL が生成されます。
URL は以下のような内容になっています。
https://signin.aws.amazon.com/federation?Action=login&Issuer=Example.org&Destination=https%3A//us-west-2.console.aws.amazon.com/cloudshell&SigninToken=IjEOpc03C6kb5d417s以下略
フェデレーションサインインの実施
生成された URL をシークレットウインドウで開いてみます。
特にサインインのプロセス(パスワードを入力するなど)を経ず、オレゴンリージョンの AWS CloudShell のコンソールに遷移しました。
そのまま AWS CloudShell 上でプリンシパルを確認すると AssumeRoled セッションであることがわかります。
$ aws sts get-caller-identity { "UserId": "AROAQ3BIIH73W5JO32XAA:Test-Session", "Account": "000000000000", "Arn": "arn:aws:sts::000000000000:assumed-role/Test-RoleA/Test-Session" }
画面右上を確認するとフェデレーテッドユーザーとしてサインインしていることがわかります。ユーザー名はロール名+セッション名ですね。
もちろんここからマネジメントコンソールのホームに遷移したりリージョンを変更したりと、通常の IAM ユーザーと同じ感覚でコンソールを操作できます。
どこまで操作できるかは、AssumeRole したロール(今回で言えばTest-RoleA
)に付与された権限次第です。
この URL はどのくらいの時間有効なの?
今回のケースではこのセッションが有効なのは 1 時間のみです。これを延ばしたい場合、スクリプトで AssumeRole する際にDurationSeconds
で希望する秒数を指定してください。
あわせて、IAM ロールの最大セッション時間を延ばしておくこともお忘れなく。
終わりに
AWS CloudShell 上でフェデレーションサインイン用の URL を生成してみました。
AWS CloudShell 特有の要素は特にないのですが、スクリプトさえ用意すれば Python や各種ライブラリがセットアップ済みなのでサッと実行できる、というところが嬉しいポイントです。
ユースケースは限られるかもしれませんが、わざわざ IAM ユーザーや IAM ロールを新規作成するほどの手間はかけたくない……!というときに方式の一つとして思い出してもらえればと思います。
以上、 チバユキ (@batchicchi) がお送りしました。