弊社では全社員が G Suite のアカウントを持っており、AWS マネジメントコンソールにアクセスする際は Google を Identity Provider として SAML 認証してシングルサインオン 1 をしている。この場合、STS の Assume Role という仕組み 2 を使って一時的な認証情報を入手し、フェデレーションユーザとしてマネジメントコンソールにアクセスすることになる。マネジメントコンソールにアクセスするために社員ごとに IAM ユーザを作成する必要がなくなるため、アカウント管理の手間が削減できる。
ただ、シングルサインオンを導入した場合にひとつだけ困るのがアクセスキーである。そもそも IAM ユーザが存在しないため、(永続的な) アクセスキーを発行することができない。ローカル端末にあるファイルを AWS CLI を用いて S3 に転送したいときや、Jupyter Notebook から boto3 を用いて AWS リソースにアクセスしたいときなど、アクセスキーが必要になるケースは少なくない。3
そこで今回は、SAML 認証による一時的な認証情報で boto3 を利用する方法についてまとめる。
内容としては、SAML 認証で AWS CLI を使う方法が公式ドキュメントに書かれている 4 ので、それを Python (boto3) に置き換えただけである。
Role ARN と IdP ARN を用意する
Role ARN は Assume Role で利用している Role の ARN で、IdP ARN は SAML 認証の Identity Provider の ARN。
role_arn = 'arn:aws:iam::123456789012:role/GSuiteMember'
idp_arn = 'arn:aws:iam::123456789012:saml-provider/Google'
SAML Response を取得する
トラブルシューティングのためにブラウザで SAML レスポンスを表示する方法 - AWS Identity and Access Management
これに従ってブラウザ上で SAML 認証を行うことで、SAML Resnponse という文字列を得ることができる。
saml_response = '...とても長い文字列...'
SAML Response は Base64 エンコードされており、とても長い文字列なのでコピペする際は注意が必要。
アクセスキーを取得する
ここまでの情報があれば、AssumeRoleWithSAML という API 5 を用いて一時的なアクセスキーを得ることができる。この操作は SAML 認証をしてから5分以内に行う必要がある。
sts = boto3.client('sts')
response = sts.assume_role_with_saml(
RoleArn=role_arn,
PrincipalArn=idp_arn,
SAMLAssertion=saml_response,
DurationSeconds=43200, # 12 hours
)
credentials = response['Credentials']
AssumeRoleWithSAML で取得したセッションはデフォルトでは1時間で失効するが、 DurationSeconds
を指定することでこれを伸ばすことができる。(上限値は Role の Maximum session duration に設定されている時間まで)
アクセスキーを利用する
あとは通常通りアクセスキーを用いて AWS リソースにアクセスすることができる。
session = boto3.session.Session(
aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken'],
region_name='ap-northeast-1',
)
# EC2 インスタンスの一覧を取得する
ec2 = session.client('ec2')
ec2.describe_instances()
awscli を使う場合は以下の出力結果を ~/.aws/credentials
に書き込むと良い。
profile = "tmp"
print("\n".join([
f"[{profile}]",
f"aws_access_key_id = {credentials['AccessKeyId']}",
f"aws_secret_access_key = {credentials['SecretAccessKey']}",
f"aws_session_token = {credentials['SessionToken']}"
]))
-
そもそも強い権限を持った アクセスキーをローカル端末に保存するのはセキュリティリスクでしかないという話はある ↩