はじめに
こんにちは。カミナシでソフトウェアエンジニアをしている佐藤です。
みなさんは、アプリケーションのフロントエンドから、Amazon S3 にファイルをアップロードするときに、どのような方法を用いているでしょうか? 「バックエンドのサーバーにファイルを送信し、バックエンドのサーバー経由で S3 にアップロードしている」「Presigned URL を払い出して、フロントエンドから直接 PUT している」など、いくつかの方法があると思います。 弊社で提供しているサービス「カミナシレポート」でも、用途に応じて上記の方法を使い分けて S3 へのファイルのアップロードを行っています。 特に、Presigned URL は、手軽に利用できる上に、バックエンドのサーバーの負荷やレイテンシーの削減といったメリットも大きく、重宝しています。
一方で、その手軽さの反面、アップロードに際して様々な制約をかけたい時に「かゆいところに手が届かない」と感じることもあります。
本記事は、このような時に Presigned URL の代替案となりうる「POST Policy」について入門を試みたものです。POST Policy の概要や実際の実装例・使用例をまとめていきます。
対象読者
- Amazon S3 へのファイルアップロードの選択肢を増やしたい方(本記事では、POST Policy メインで解説しますが、冒頭で Presigned URL にも軽く触れます)
- Presigned URL を利用した PUT によるファイルアップロードを使っている中で、より柔軟な制御の必要性を感じたことがある方
- 「POST Policy」によるファイルアップロードの使用例・実装例が知りたい方
Presigned URL とは
POST Policy を紹介する前に、Presigned URL のおさらいをしておきましょう。
Presigned URL とは端的にいうと、「対象の S3 のオブジェクトにアクセスするための AWS の IAM Credential を持たない主体(ユーザー、フロントエンドアプリケーション etc)が、同オブジェクトを参照または更新できるようにするための仕組み」と言えます。
(*本記事は、ファイルの「アップロード」をメインテーマとしていますが、Presigned URL は、「ダウンロード」「アップロード」双方に対応しています)
以下が Presigned URL の例です。
https://your-bucket-name.s3.ap-northeast-1.amazonaws.com/2024022614/my-sample-file.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=HOGEHOGE%2F20240226%2Fap-northeast-1%2Fs3%2Faws4_request&X-Amz-Date=20240226T142939Z&X-Amz-Expires=60&X-Amz-Security-Token=IQo...3D&X-Amz-SignedHeaders=host&X-Amz-Signature=032a...17
Amazon S3 のエンドポイントに向けた、署名が付与された URL であることがわかります。
このような URL を、「対象の S3 オブジェクトにアクセスするための AWS の IAM Credential を有する主体(バックエンドのアプリケーション etc)」が署名・作成し、先述の「ユーザー」や「フロントエンドアプリケーション」などに受け渡します。 ユーザーやフロントエンドアプリケーションは、この URL に対して、GET や PUT の HTTP リクエストを送信することで、対象の S3 オブジェクトの参照や更新が行えます。
ファイルをダウンロード・アップロードする際に、バックエンドのアプリケーションを経由しなくて良いため、バックエンドのサーバーの負荷やレイテンシーの削減につながるなどのメリットがあります。
Presigned URL についてより詳細な情報は、Amazon S3 - User Guide の Working with presigned URLs をご確認ください。
Presigned URL は手軽に無駄の少ない処理が実現できる非常に便利なものですが、しばしばファイルアップロードに際して、様々な制約をかけたくなることがあります。
- アップロードできるオブジェクトのサイズを、自サービスのユースケースで必要十分な範囲に制限したい。
- アップロード時の Content-Type にある程度の柔軟性を持たせた上で、特定の値を強制したい。
上記のような制約は(少なくとも私の知る限りでは)、 Presigned URL を使用したアップロードではかけることができません。
このように、Presigned URL だと「かゆいところに手が届かない」と思った時が、まさに本記事のメインテーマである「POST Policy」の出番です。次項以降で、詳しくみていきましょう。
POST Policy とは
Amazon S3 の API Reference で紹介されている方法で、様々なポリシーが記載された JSON 形式の「policy document」と「policy document」への署名とともに、ファイルをアップロードすることができます。この「policy document」が、「POST Policy」と呼ばれています。 アップロードしたいファイルを署名とともに送信する点は、Presigned URL を利用した PUT によるファイルアップロードとよく似ていますが、以下のような点が異なります。
- HTTP メソッドは、POST
- リクエストボディは、multipart/form-data 形式であり、policy document や、署名、アップロード対象のファイルなどが、個々のフィールドにセットされる。
- policy document に様々な制約を記載できる。例えば、オブジェクトのキーやアップロードするデータのサイズへの制約を記述できる。
以下は、policy document の例です。
{ "expiration": "2024-02-20T00:00:00.000Z", "conditions": [ {"bucket": "myExampleBucket"}, ["eq", "$key", "20240219/abc"], ["starts-with", "$Content-Type", "image/"], ["content-length-range", 0, 10240], {"x-amz-credential": "ABCDEFG1234567/20240219/ap-northeast-1/s3/aws4_request"}, {"x-amz-algorithm": "AWS4-HMAC-SHA256"}, {"x-amz-date": "20240219T000000Z" } ] }
この例では、アップロード対象のオブジェクトのキーは「20240219/abc」に制限され、「Content-Type」は、「image/」で始まるものに制限されます。また、アップロードされるデータのサイズは、0〜10KiB に制限されます。
処理のフローは、Presigned URL の場合と非常によく似ています。バックエンドアプリケーションなどが、 policy document に、AWS Signature Version 4 で署名をし、policy document などとともに、ユーザーやフロントエンドアプリケーションに受け渡します。 ユーザーやフロントエンドアプリケーションは、これらをファイルデータとともに POST します。
以降で、実際の例をみながら、理解を深めていきます。
実装例
Go による実装例・使用例を通して、理解を深めていきます。本項ではコードの一部のみを示し、記事末の Appendix.1 にコード全体を記載します。また、Appendix.2 に具体的な環境構築方法や実行方法を記載します。
大まかに以下の Step で実装します。
- Step1. policy document の作成
- Step2. signing Key の作成
- Step3. policy document に対して署名
詳しくそれぞれの Step をみていきましょう。
📝 Step1. policy document の作成
/* Step1. Policy document の作成 */ policy := Policy{ Expiration: time.Now().Add(1 * time.Hour).Format("2006-01-02T15:04:05Z"), Conditions: []any{ Cond{"bucket": bucket}, Cond{"acl": "private"}, []string{"eq", "$key", objectKey}, []string{"starts-with", "$Content-Type", "image/"}, []any{"content-length-range", 0, 10 * 1024}, Cond{"x-amz-algorithm": algorithm}, Cond{"x-amz-credential": xAmzCredential}, Cond{"x-amz-date": xAmzDate}, Cond{"x-amz-security-token": sessionToken}, }, }
まずは policy document を作成します。expiration、conditions は必須であり、conditions の中でも以下のフィールドは必須とされています。
- x-amz-algorithm
- x-amz-credential
- x-amz-date
- x-amz-security-token (「temporary security credentials」を利用する場合)
AWS Signature Version 4 では、AssumeRole などによりクレデンシャルを取得した場合など、「temporary security credentials」を利用する場合は x-amz-security-token も必要となるようです。実際、今回の実装例では、このフィールドを含めなければ、ファイルアップロード時にエラーとなりました( 参考 )。
また、conditions 内の各種条件は「完全一致」(Exact Matches)、「前方一致」(Starts With)などのいくつかの方法で指定することが可能です。
conditions の書き方などの詳細は、API Reference の Condition Matching の部分をご確認ください。
今回は以下のような、policy document を生成しています。
{ "expiration": "2024-02-21T00:47:39Z", "conditions": [ { "bucket": "your-bucket-name" }, { "acl": "private" }, ["eq", "$key", "20240220/9c83744b-d463-4b6a-9d9f-dd634999fee3"], ["starts-with", "$Content-Type", "image/"], ["content-length-range", 0, 10240], { "x-amz-algorithm": "AWS4-HMAC-SHA256" }, { "x-amz-credential": "YOURACCESSKEYIDHOGE/20240220/ap-northeast-1/s3/aws4_request" }, { "x-amz-date": "20240220T000000Z" }, { "x-amz-security-token": "IQoJ...1m" } ] }
*オブジェクトのキーをクライアントサイドで自由に指定できるようにすることは、大きなセキュリティリスクにつながる可能性があるため、よほどの理由がない限り、通常は「eq」で完全一致で指定する=サーバーサイドでキーを決定するべきでしょう( 参考 )。
conditions に指定できる要素についての詳細は、API Reference の Conditions の部分をご確認ください。
📝 Step2. signing Key の作成
func createSigningKey(secretAccessKey string, date time.Time, awsRegion string, awsService string) []byte { // DateKey = HMAC-SHA256("AWS4" + "<SecretAccessKey>", "<yyyymmdd>") dateKey := hash([]byte("AWS4"+secretAccessKey), date.Format("20060102")) // DateRegionKey = HMAC-SHA256(DateKey, "<aws-region>") dateRegionKey := hash(dateKey, awsRegion) // DateRegionServiceKey = HMAC-SHA256(DateRegionKey, "<aws-service>") dateRegionServiceKey := hash(dateRegionKey, awsService) // SigningKey = HMAC-SHA256(DateRegionServiceKey, "aws4_request") signingKey := hash(dateRegionServiceKey, "aws4_request") return signingKey }
アクセスキーを元に、署名用のキーを作成します。 API Reference の Calculating a Signature を参考に実装しています。
📝 Step3. policy document に対して署名
/* Step3. policy document に対して署名 */
base64EncodedSecurityPolicy := toBase64EncodedSecurityPolicy(policy)
signature := createSignature(signingKey, base64EncodedSecurityPolicy)
Step2. で作成した signing Key を利用して、policy document を Base64 エンコードしたものに署名します。
以上が、大まかな実装の Step です。
なお、このアプリケーションは検証のために作成したスタンドアローンなアプリケーションですが、実際の Web アプリケーションなどでは、上記のような処理を実装したエンドポイントが作成され、PostFormComponents
に相当する内容が、クライアントアプリケーションに渡されるイメージです。
実行してみる
前項目のサンプルコードを実行すると、以下のように出力されます。
==== PostFormComponents ==== export AWS_BUCKET="your-bucket-name" export OBJECT_KEY="20240218/6107cc12-d3f3-4544-8026-b94998ef999a" export X_AMZ_CREDENTIAL="YOURACCESSKEYIDHOGE/20240218/ap-northeast-1/s3/aws4_request" export X_AMZ_ALGORITHM="AWS4-HMAC-SHA256" export X_AMZ_DATE="20240218T000000Z" export POLICY="........" export X_AMZ_SIGNATURE="........" export X_AMZ_SECURITY_TOKEN="........" ==== PostFormComponents ====
この出力値を元に環境変数を設定し、以下のようなスクリプト( post.sh )を用いて、動作を確認してみましょう。
ACL="${ACL:-private}" FILE="${FILE:-data.png}" CONTENT_TYPE="${CONTENT_TYPE:-image/png}" curl -X POST \ -H "Host: ${AWS_BUCKET}.s3.amazonaws.com" \ -H 'Content-Type: multipart/form-data; boundary=1213456789abcdedg' \ -F "key=${OBJECT_KEY}" \ -F "Content-Type=${CONTENT_TYPE}" \ -F "acl=${ACL}" \ -F "X-Amz-Credential=${X_AMZ_CREDENTIAL}" \ -F "X-Amz-Algorithm=${X_AMZ_ALGORITHM}" \ -F "X-Amz-Date=${X_AMZ_DATE}" \ -F "Policy=${POLICY}" \ -F "X-Amz-Signature=${X_AMZ_SIGNATURE}" \ -F "X-Amz-Security-Token=${X_AMZ_SECURITY_TOKEN}" \ -F "file=@./${FILE}" \ -w "\n%{http_code}\n" https://${AWS_BUCKET}.s3.amazonaws.com/
実行結果(見やすさのため、改行などは適当に調整)
#### data.png という 10240 byte 以下のサイズのファイルを送信 $ sh ./post.sh 204 #### binary_file_10240.bin という 10240 byte ちょうどのファイルを送信 $ FILE=binary_file_10240.bin sh ./post.sh 204 #### binary_file_10241.bin という 10240 byte を超えるファイルを送信 $ FILE=binary_file_10241.bin sh ./post.sh <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>EntityTooLarge</Code> <Message>Your proposed upload exceeds the maximum allowed size</Message> <ProposedSize>10241</ProposedSize> <MaxSizeAllowed>10240</MaxSizeAllowed> <RequestId>...(略)...</RequestId> <HostId>...(略)...</HostId> </Error> 400 #### policy document で許可されていない Content-Type を指定(前方一致) $ CONTENT_TYPE="text/plan" sh ./post.sh <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>AccessDenied</Code> <Message>Invalid according to Policy: Policy Condition failed: ["starts-with", "$Content-Type", "image/"]</Message> <RequestId>...(略)...</RequestId> <HostId>...(略)...</HostId> </Error> 403 #### policy document で許可されていないオブジェクトのキーを指定(完全一致) $ OBJECT_KEY="my-key-123" sh ./post.sh <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>AccessDenied</Code> <Message>Invalid according to Policy: Policy Condition failed: ["eq", "$key", "20240218/6107cc12-d3f3-4544-8026-b94998ef999a"]</Message> <RequestId>...(略)...</RequestId> <HostId>...(略)...</HostId> </Error> 403 #### policy document で conditions に指定されていないフィールド "x-amz-meta-uuid=hoge" を Form Field に追加して、実行 $ sh ./post.sh <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>AccessDenied</Code> <Message>Invalid according to Policy: Extra input fields: x-amz-meta-uuid</Message> <RequestId>...(略)...</RequestId> <HostId>...(略)...</HostId> </Error> 403
下記のような結果が得られたことがわかると思います。
- content-length-range で指定したサイズの範囲内のファイルはアップロードでき、範囲を超えるサイズのファイルはアップロードできない
- policy document の conditions で指定された形式を満たさないフィールドがある場合、アップロードできない
- policy document の conditions で指定されていないフィールドが存在する場合、アップロードできない
個人的に嬉しいと思ったのが、policy document の conditions に指定がないフィールドは( 一部例外を除き )、POST 時に指定できない点です。つまり、policy document の conditions はホワイトリストになっているということです。 これにより、「 policy document に記載をし忘れて、意図しない指定でアップロードされてしまう」事態が発生しづらくなっていると思います。
一方で、以下のような点には注意が必要だと感じました。
- policy document の conditions で、content-length-range の指定を忘れない。
- policy document の conditions の条件指定は、できるだけ「完全一致」で記述する。特に、ワイルドカード=制限なし( 例:["starts-with", "$acl", ""])は、可能な限り避ける。
content-length-range について、この指定を行わなくてもアップロードは成功することから、Amazon S3 のリミットを超過しない限りアップロードは成功すると思われます(PUT と同様であるならば、5GB)。
実行してみる - おまけ
さらに2パターンほど試してみました。policy document を改変したパターンと、AssumeRole した IAM Role に適切な permission がないパターンです。
まずは、policy document の一部を書き換えて送信したパターンです。
#### policy document を書き換え $ POLICY=${DUMMY_POLICY} sh ./post.sh <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>SignatureDoesNotMatch</Code> <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message> <AWSAccessKeyId>YOURACCESSKEYIDHOGE</AWSAccessKeyId> <StringToSign>eyJl....cifV19</StringToSign> <SignatureProvided>1478....48ca</SignatureProvided> <StringToSignBytes>65 79 .... 31 39</StringToSignBytes> <RequestId>...(略)...</RequestId> <HostId>...(略)...</HostId> </Error> 403
次に、AssumeRole した IAM Role に適切な permission がないパターンです。
今回作成したアプリケーションでは、以下の IAM Policy が付与された IAM Role を AssumeRole しています。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:PutObject" ], "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}/*", "Effect": "Allow" } ] }
Signature などを生成したのち、この IAM Policy の Action の部分を s3:GetObject に変更して、POST 実行してみました。
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": "arn:aws:s3:::{YOUR_BUCKET_NAME}/*", "Effect": "Allow" } ] }
#### IAM Policy の Action 部分を s3:GetObject 更新した後に実行 $ sh ./post.sh <?xml version="1.0" encoding="UTF-8"?> <Error> <Code>AccessDenied</Code> <Message>Access Denied</Message> <RequestId>...(略)...</RequestId> <HostId>...(略)...</HostId> </Error> 403
なお、この後、IAM Policy の内容を元に戻して実行すると、POST は成功しました。 Signature を生成した後で、IAM Policy を書き換えて実行すると、POST の成功・失敗が切り替わることから、AssumeRole した IAM Role の permission を、POST の実行時に評価していることがわかります。
まとめ
いかがでしたでしょうか? POST Policy を用いることで、Amazon S3 へのファイルアップロードの実装方法の選択肢が広がりそうですね!
最後に宣伝です📣
カミナシでは絶賛採用中です!一緒に最高のサービスを作っていく仲間を募集しています!
参考サイト
- Amazon S3 - User Guide の Working with presigned URLs
- Amazon S3 - API Reference の Authenticating Requests (AWS Signature Version 4) と Browser-Based Uploads Using POST (AWS Signature Version 4)
- AWS IAM - User Guide の Signing AWS API requests
- Amazon S3の脆弱な利用によるセキュリティリスクと対策 - Flatt Security Blog
Appendix.1
本記事で使用した GO アプリケーションのコード全体です。
package main import ( "context" "crypto/hmac" "crypto/sha256" "encoding/base64" "encoding/json" "fmt" "os" "time" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/google/uuid" "github.com/joho/godotenv" ) type Policy struct { Expiration string `json:"expiration"` Conditions []any `json:"conditions"` } type Cond = map[string]string type PostFormComponents struct { Bucket string `json:"bucket"` ObjectKey string `json:"objectKey"` Policy string `json:"policy"` XAmzAlgorithm string `json:"xAmzAlgorithm"` XAmzCredential string `json:"xAmzCredential"` XAmzDate string `json:"xAmzDate"` XAmzSignature string `json:"xAmzSignature"` XAmzSecurityToken string `json:"xAmzSecurityToken"` } func main() { loadEnv() currentTime := time.Now() bucket := os.Getenv("AWS_BUCKET") objectKey := currentTime.Format("20060102") + "/" + generateUUIDv4() awsRegion := os.Getenv("AWS_REGION") awsService := "s3" algorithm := "AWS4-HMAC-SHA256" accessKeyId, secretAccessKey, sessionToken := getTemporarySecurityCredentials() xAmzCredential := createXAmzCredential( accessKeyId, currentTime, awsRegion, awsService, ) xAmzDate := createXAmzDate(currentTime) /* Step1. Policy document の作成 */ policy := Policy{ Expiration: time.Now().Add(1 * time.Hour).Format("2006-01-02T15:04:05Z"), Conditions: []any{ Cond{"bucket": bucket}, Cond{"acl": "private"}, []string{"eq", "$key", objectKey}, []string{"starts-with", "$Content-Type", "image/"}, []any{"content-length-range", 0, 10 * 1024}, Cond{"x-amz-algorithm": algorithm}, Cond{"x-amz-credential": xAmzCredential}, Cond{"x-amz-date": xAmzDate}, Cond{"x-amz-security-token": sessionToken}, }, } /* Step2. Signing Key の作成 */ signingKey := createSigningKey( secretAccessKey, time.Now(), awsRegion, awsService, ) /* Step3. policy document に対して署名 */ base64EncodedSecurityPolicy := toBase64EncodedSecurityPolicy(policy) signature := createSignature(signingKey, base64EncodedSecurityPolicy) postBodyComponents := PostFormComponents{ Bucket: bucket, ObjectKey: objectKey, Policy: base64EncodedSecurityPolicy, XAmzAlgorithm: algorithm, XAmzCredential: xAmzCredential, XAmzDate: xAmzDate, XAmzSignature: signature, XAmzSecurityToken: sessionToken, } postBodyComponents.Output() } func getTemporarySecurityCredentials() (accessKeyId string, secretAccessKey string, sessionToken string) { input := &sts.AssumeRoleInput{ RoleArn: aws.String(os.Getenv("ROLE_ARN_TO_ASSUME")), RoleSessionName: aws.String("session-" + generateUUIDv4()), } cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } client := sts.NewFromConfig(cfg) result, err := client.AssumeRole(context.TODO(), input) if err != nil { panic(err) } return *result.Credentials.AccessKeyId, *result.Credentials.SecretAccessKey, *result.Credentials.SessionToken } func createXAmzCredential(accessKeyID string, date time.Time, awsRegion string, awsService string) string { return fmt.Sprintf("%s/%s/%s/%s/aws4_request", accessKeyID, date.Format("20060102"), awsRegion, awsService) } func createXAmzDate(date time.Time) string { return date.Format("20060102T000000Z") } func toBase64EncodedSecurityPolicy(policy Policy) string { policyJSON, err := json.Marshal(policy) if err != nil { panic(err) } return base64.StdEncoding.EncodeToString(policyJSON) } func createSigningKey(secretAccessKey string, date time.Time, awsRegion string, awsService string) []byte { // DateKey = HMAC-SHA256("AWS4" + "<SecretAccessKey>", "<yyyymmdd>") dateKey := hash([]byte("AWS4"+secretAccessKey), date.Format("20060102")) // DateRegionKey = HMAC-SHA256(DateKey, "<aws-region>") dateRegionKey := hash(dateKey, awsRegion) // DateRegionServiceKey = HMAC-SHA256(DateRegionKey, "<aws-service>") dateRegionServiceKey := hash(dateRegionKey, awsService) // SigningKey = HMAC-SHA256(DateRegionServiceKey, "aws4_request") signingKey := hash(dateRegionServiceKey, "aws4_request") return signingKey } func createSignature(signingKey []byte, stringToSign string) string { signature := hash(signingKey, stringToSign) return fmt.Sprintf("%x", signature) } func (p PostFormComponents) Output() { fmt.Println("==== PostFormComponents ====") exportToEnv("AWS_BUCKET", p.Bucket) exportToEnv("OBJECT_KEY", p.ObjectKey) exportToEnv("X_AMZ_CREDENTIAL", p.XAmzCredential) exportToEnv("X_AMZ_ALGORITHM", p.XAmzAlgorithm) exportToEnv("X_AMZ_DATE", p.XAmzDate) exportToEnv("POLICY", p.Policy) exportToEnv("X_AMZ_SIGNATURE", p.XAmzSignature) exportToEnv("X_AMZ_SECURITY_TOKEN", p.XAmzSecurityToken) fmt.Println("==== PostFormComponents ====") } func loadEnv() { err := godotenv.Load(".env") if err != nil { panic(err) } } func generateUUIDv4() string { return uuid.NewString() } func hash(secretKey []byte, message string) []byte { key := []byte(secretKey) h := hmac.New(sha256.New, key) h.Write([]byte(message)) hashed := h.Sum(nil) return hashed } func exportToEnv(key, value string) { if value == "" { return } fmt.Println("export " + key + "=\"" + value + "\"") }
Appendix.2
今回作成した Go アプリケーションの実行方法を紹介します。 今回は、AWS Cloud9 を利用して実行しました。
環境構築の大まかな手順は以下の通りです。
- Amazon S3 のバケット、AWS Cloud9 の EC2 にアタッチする IAM Role および、アプリケーションが、AssumeRole する IAM Role を作成する。
- Amazon Cloud9 環境を作成し、Go の実行環境を整える(参考)
- Amazon Cloud9 の AWS Settings において、「AWS managed temporary credentials」を OFF にする。
- Amazon Cloud9 の EC2 インスタンスに、「1.」で作成した、EC2 用の IAM Role をアタッチする。
- Go アプリケーションの環境変数には、region および、上記までで作成した Amazon S3 のバケット名、アプリケーション内で AssumeRole するための IAM Role の ARN が設定されるようにする。
「1.」のリソースを作成する CloudFormation のテンプレート
AWSTemplateFormatVersion: 2010-09-09 Parameters: S3BucketName: Type: String Cloud9EC2RoleName: Type: String Default: "my-post-policy-study-cloud9-ec2-role" PostObjectRoleName: Type: String Default: "my-post-policy-study-post-object-role" Resources: MyS3Bucket: Type: AWS::S3::Bucket Properties: BucketName: !Ref S3BucketName Cloud9EC2Role: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: ec2.amazonaws.com Action: sts:AssumeRole RoleName: !Ref Cloud9EC2RoleName Policies: - PolicyName: !Sub ${Cloud9EC2RoleName}-policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - sts:AssumeRole Resource: !Sub arn:aws:iam::${AWS::AccountId}:role/${PostObjectRoleName} Cloud9EC2RoleIAMInstanceProfile: Type: "AWS::IAM::InstanceProfile" Properties: Path: "/" InstanceProfileName: !Ref Cloud9EC2Role Roles: - !Ref Cloud9EC2Role PostObjectRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: AWS: !GetAtt Cloud9EC2Role.Arn Action: sts:AssumeRole RoleName: !Ref PostObjectRoleName Policies: - PolicyName: !Sub ${PostObjectRoleName}-policy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:PutObject Resource: !Sub arn:aws:s3:::${S3BucketName}/* Outputs: RoleARN: Description: The ARN of the IAM role for your application to assume. Value: !GetAtt PostObjectRole.Arn
「5.」で設定する環境変数の例
AWS_REGION="ap-northeast-1" AWS_BUCKET="{YOUR_BUCKET_NAME}" ROLE_ARN_TO_ASSUME="arn:aws:iam::{YOUR_ACCOUNT_ID}:role/my-post-policy-study-post-object-role"