AWSにおけるSTSを使ったクロスアカウントアクセスについて
こんにちは。CURUCURUエンジニアの長尾です。 今日はAWSのSTSを使ったクロスアカウントのS3アクセスについてお話したいと思います。
クロスアカウントについて
AWSでは企業や個人がアカウントを複数作成してプロジェクトを開発したりするのがスタンダードですが、 複数のアカウント間でのリソースアクセスするケースがあり、そういう使い方をクロスアカウントアクセスと言ったりします。
STSについて
AWSでは通常API経由でのアクセスをするにはアクセスキーとシークレットキーが必要です。 STSは一時的なクレデンシャルを生成することができ、それを用いることでAWSリソースへのアクセスを可能とするサービスです。
クロスアカウントでS3へアクセスしてみる
STSは様々なシナリオで使われることが想定されていますが、今回はクロスアカウントS3アクセスについてやっていきたいと思います。
以下のイメージです。
前提
- アカウントA:S3を使っているアカウント
- アカウントB:アカウントAのS3を使いたいアカウント
- AWS Golang SDKを使ったアクセス
やること
- アカウントA:S3へアクセスするポリシーを作成
- アカウントA:アカウントBからassumeRoleできるロールを作成
- アカウントB:IAM設定を行い、クロスアカウントに必要な権限を付与する
- アカウントBから実際にアクセスしてみる
1. アカウントA:S3へアクセスするポリシーを作成
まずはS3へアクセスするポリシーの作成します。 下図のようにS3の読み込み権限を特定のバケット(今回はtestバケット)に付与。
「読み込み」という権限だけでも52種類あるので必要なものだけ付与してください。 (今回は便宜上全部選択しています。)
あとは名前をつけて作成完了です。
2. アカウントA:アカウントBからassumeRoleできるロールを作成
信頼されたエンティティにて「カスタム信頼ポリシー」を選択。 そこにアカウントBのユーザー名を入れる。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::{アカウントBのID}:user/{ユーザー名}"
]
},
"Action": "sts:AssumeRole"
}
]
}
3. アカウントB:IAM設定を行い、クロスアカウントに必要な権限を付与する
今回アカウントBのアクセスする側ではRoleではなくユーザーを使ってアクセスします。
assumeroleできるポリシーを作成。作成にあたってはJSONで以下のものをそのまま貼り付けてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::{アカウントAのID}:role/{ロールの名前}"
}
]
}
作成したポリシーを使いたいユーザーにアタッチしてください。
4. アカウントBから実際にアクセスしてみる
import (
"context"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/service/sts"
)
const ROLE_ARN = "arn:aws:iam::{アカウントAのID}:role/{アカウントAのロールARN}"
// 一時クレデンシャルの取得
func assumeRole(accessKey string, secretKey string, sessionToken string) (aws.Credentials, error) {
// アカウントBのユーザーのAPIアクセスキーとシークレットキー
ctx := context.Background()
cred := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, sessionToken))
if cred == nil {
panic("failed to fetch credentials")
}
cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(cred))
if err != nil {
panic(err)
}
client := sts.NewFromConfig(cfg)
provider := stscreds.NewAssumeRoleProvider(client, ROLE_ARN)
cfg.Credentials = aws.NewCredentialsCache(provider)
// アカウントAの一時クレデンシャルを取得
return cfg.Credentials.Retrieve(ctx)
}
// S3へアクセス
func accessS3(accessKey string, secretKey string, sessionToken string) {
// アカウントAのキーでアクセス
ctx := context.Background()
cred := aws.NewCredentialsCache(credentials.NewStaticCredentialsProvider(accessKey, secretKey, sessionToken))
if cred == nil {
panic("failed to fetch credentials")
}
cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(cred))
if err != nil {
panic(err)
}
client := s3.NewFromConfig(cfg)
params := &s3.GetObjectInput{
Bucket: aws.String({読み込み許可したバケット名}),
Key: aws.String({バケットに配置したファイルパス}),
}
// アカウントAのS3からファイル取得
client.GetObject(ctx, params)
}
func main() {
cred, err := assumeRole({アカウントBのユーザーアクセスキー}, {アカウントBのユーザーシークレットキー}, "")
if err != nil {
panic(err)
}
// credの中に一時クレデンシャルの'AccessKeyID', 'SecretAccessKey', 'SessionToken'が取得できるのでそれを使ってアクセス
accessS3(cred.AccessKeyID, cred.SecretAccessKey, cred.SessionToken)
}
いかがでしたでしょうか? 概念的に少しややこしいですが、STSは結構幅広く使える技術だと思うので覚えておいて損はないです。
以上、長尾がお届けしました。