AWS STSを使用して、GCPの認証情報を使ってS3にアクセスする

プログラミング

こんにちは!いっちー(@tetestkake_blog)です。

今回は「AWS STSを使用して、GCPの認証情報を使ってS3にアクセスする」というテーマを解説します。

GCPをメインに開発をしている際にも、他システムと連携する際にAWSのリソースにアクセスしたい場合があります。

その場合はAWS側でIAMを払い出して、それを使用してアクセスしても良いのですが、

AWS STSを使うとIAMを使用せずともS3へのアクセスを実現できることがわかったので、

今回はその方法について解説します。

この記事がおすすめの読者
  • GCPをメインで使っているが、AWSにアクセスする必要が出てきた人
  • STSがどんな役割を果たすのかが気になる人
  • 連携の際にIAMを切り出して渡したくない人
いっちー
いっちー

STSを使用して権限周りをよりセキュアにしていきましょう!

スポンサーリンク

AWS STSとは?

AWS STSとはAmazon Security Token Serviceの略で、

一時的な認証情報を発行するサービスになります。

一時的な認証情報には、

  • アクセスキー
  • シークレットアクセスキー
  • セッショントークン

から構成され、これらの認証情報を使って許可されたリソースへのアクセスを実現します。

今回はAWS STSを使用して、GCPのサービスアカウントを使用してAWSのS3にアクセスする実装例を紹介します。

実装例

それでは実装例を見ていきます。サンプルプログラムも配置したので参考にしてみてください。

まずはプログラム配置をする前段階のサービスアカウントの準備などを行います。

GCPでサービスアカウントを新規作成

ますはGCPでサービスアカウントを作成します。

IAMと管理 > サービスアカウント > サービスアカウントの作成から作成します。

名前などの必要な情報を入れて、権限の設定では

「Service Account → Service Account Token Creator」を選択します。

サービスアカウントを作成できたら詳細ページに移動して(サービスアカウントをクリックすると詳細ページに移動)、

詳細に表示されている「一意のID」の値を控えておきます。

この値は後ほどAWSにてロールを設定する際に使用します。

AWSでロールを作成

ここからはAWSの設定です。コンソールにログインして

「IAM > ロール > ロールの作成」からロールを作成します。

ウェブIDを選択し、IDプロバイダーをGoogleにします。

AudienceにはGCPで作成したサービスアカウントの「一意のID」を記入します。

「次のステップ:アクセス権限」をクリックします。

今回はS3に配置したファイルの読み取りを確認したいので、「AmazonS3ReadOnlyAccess」を付与します。

タグはオプションなので今回はスキップして、ロールを作成します。

ロールのARNはプログラムから使用するので控えておきます。

GCEのインスタンスを立ててプログラムを配置

ようやくロール周りの設定ができたので、GCEにサーバーを立ててプログラムからアクセスします。

GCPのサーバーに配置するプログラムはGoで記述しました。

GCPサーバーにはGoやGitをインストールしましたがここでは詳細については割愛します。

今回はS3にサンプルファイルを設置してそれを読み取るプログラムを作成しました。

main.go

package main

import (
	"fmt"
	"io/ioutil"

	"cloud.google.com/go/compute/metadata"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/sts"
)

func main() {
	awsRoleArn := "<作成したロールのARN>"

	// メタデータサーバーから値を取得
	instanceName := getMetadata("instance", "name")
	projectId := getMetadata("project", "project-id")
	projectAndInstanceName := fmt.Sprintf("%s.%s", projectId, instanceName)
	token := getMetadata("instance", "service-accounts/default/identity?format=standard&audience=gcp")

	sess, err := session.NewSession(&aws.Config{
		Region: aws.String("<リージョン>"),
	})
	if err != nil {
		fmt.Println("NewSession Error", err)
		return
	}

	// STS Clientを作成
	svc := sts.New(sess)
	result, err := svc.AssumeRoleWithWebIdentity(&sts.AssumeRoleWithWebIdentityInput{
		RoleArn:          &awsRoleArn,
		RoleSessionName:  &projectAndInstanceName,
		WebIdentityToken: &token,
	})
	if err != nil {
		fmt.Println("AssumeRoleWithWebIdentity Error", err)
		return
	}

	accessKeyId := *result.Credentials.AccessKeyId
	secretAccessKey := *result.Credentials.SecretAccessKey
	sessionToken := *result.Credentials.SessionToken
	expiration := result.Credentials.Expiration

	fmt.Println(fmt.Sprintf("accesskey:%s, secretkey:%s, sessiontoken:%s, expiration:%v", accessKeyId, secretAccessKey, sessionToken, expiration))

	creds := credentials.NewStaticCredentials(accessKeyId, secretAccessKey, sessionToken)
	sess2, err := session.NewSession(&aws.Config{
		Credentials: creds,
		Region:      aws.String("<リージョン>")},
	)
	if err != nil {
		fmt.Println("NewSession with credentials Error", err)
		return
	}
	client := s3.New(sess2)

	bucketName := "<バケット名>"
	objectKey := "<ファイル名>"
	obj, err := client.GetObject(&s3.GetObjectInput{
		Bucket: aws.String(bucketName),
		Key:    aws.String(objectKey),
	})
	if err != nil {
		fmt.Println(err)
		return
	}

	// 内容を読み込んで表示
	byteArray, _ := ioutil.ReadAll(obj.Body)
	fmt.Println(string(byteArray))
}

func getMetadata(path, parameter string) string {
	data, _ := metadata.Get(fmt.Sprintf("%s/%s", path, parameter))
	return data
}

少しづつ解説していきます。

以下ではメタデータサーバーから値を取得しています。これはのちにSTSクライアントを作成する際に使用します。

// メタデータサーバーから値を取得
instanceName := getMetadata("instance", "name")
projectId := getMetadata("project", "project-id")
projectAndInstanceName := fmt.Sprintf("%s.%s", projectId, instanceName)
token := getMetadata("instance", "service-accounts/default/identity?format=standard&audience=gcp")

以下の部分で取得したメタデータを使用してSTSクライアントを作成しています。

// STS Clientを作成
svc := sts.New(sess)
result, err := svc.AssumeRoleWithWebIdentity(&sts.AssumeRoleWithWebIdentityInput{
	RoleArn:          &awsRoleArn,
	RoleSessionName:  &projectAndInstanceName,
	WebIdentityToken: &token,
})

resultにて返ってきた値から一時的なアクセストークンやシークレットアクセストークンを取得できます。

accessKeyId := *result.Credentials.AccessKeyId
secretAccessKey := *result.Credentials.SecretAccessKey
sessionToken := *result.Credentials.SessionToken
expiration := result.Credentials.Expiration

fmt.Println(fmt.Sprintf("accesskey:%s, secretkey:%s, sessiontoken:%s, expiration:%v", accessKeyId, secretAccessKey, sessionToken, expiration))

クレデンシャルはaccessKeyId、secretAccessKey、sessionTokenを作って作成します。

creds := credentials.NewStaticCredentials(accessKeyId, secretAccessKey, sessionToken)
sess2, err := session.NewSession(&aws.Config{
	Credentials: creds,
	Region:      aws.String("<リージョン>")},
)
if err != nil {
	fmt.Println("NewSession with credentials Error", err)
	return
}
client := s3.New(sess2)

その後は通常のS3を操作する方法で実装を進めることができます。

最終行でS3のファイルの内容を表示しています。

bucketName := "<バケット名>"
objectKey := "<ファイル名>"
obj, err := client.GetObject(&s3.GetObjectInput{
	Bucket: aws.String(bucketName),
	Key:    aws.String(objectKey),
})
if err != nil {
	fmt.Println(err)
	return
}

// 内容を読み込んで表示
byteArray, _ := ioutil.ReadAll(obj.Body)
fmt.Println(string(byteArray))
いっちー
いっちー

プログラム実行時にファイルの内容が表示されたら成功です!

お疲れ様でした。

さいごに

今回は「AWS STSを使用して、GCPの認証情報を使ってS3にアクセスする」というテーマを解説しました。

AWS側からセキュリティなどの理由でIAMを払い出したくない時など、今回紹介したSTSを使った方法が使用できるので、その際はぜひ試してみてください!

おすすめ書籍

図解即戦力 Amazon Web Servicesのしくみと技術がこれ1冊でしっかりわかる教科書

Amazon Web Services 基礎からのネットワーク&サーバー構築 改訂3版 

お知らせ

にほんブログ村のランキングに参加しています。よろしければ下のボタンをポチッとしていただけると嬉しいです\(^^)/

にほんブログ村 IT技術ブログへ
にほんブログ村

参考リンク

【そんなときどうする?】別のアカウントにセキュアにアクセスしたい! いまさらきけないSTSとは?

Assume an AWS Role from a Google Cloud Instances without using IAM keys

GitHub doitintl/janus

コメント

タイトルとURLをコピーしました