一時的セキュリティ認証情報を使って、特定のS3バケットにアップロードする

「一時的セキュリティ認証情報」とは:
> http://aws.amazon.com/jp/iam/faqs/#How_can_temporary_security_credentials_be_used_to_call_AWS_service_APIs
Q: 一時的セキュリティ認証情報とは何ですか?
一時的なセキュリティ認証情報は、アクセスキー ID、シークレットアクセスキー、およびセキュリティトークンで構成されています。これらは指定された期間と特定のアクセス許可一式に有効です。一時的なセキュリティ証明書は、単に「トークン」と呼ばれることがあります。トークンは、IAM ユーザー用に、または自身の社内ディレクトリで管理するフェデレーティッドユーザー用にリクエストすることができます。

## 準備

### リソース作成
以下のリソースを、CloudFormationで作成する。

* アップロード先のS3バケット
* クレデンシャルを発行するIAMユーザー

```json:sample.template
{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "Sample Template S3_Bucket and IAM_User for federation.",

  "Resources" : {

    "S3PrivateBucket" : {
      "Type"       : "AWS::S3::Bucket",
      "Properties" : { "AccessControl" : "Private" }
    },

    "Publisher" : { "Type" : "AWS::IAM::User" },

    "PublisherPolicy" : {
      "Type"       : "AWS::IAM::Policy",
      "Properties" : {
        "Users"          : [ { "Ref" : "Publisher" } ],
        "PolicyName"     : "PublisherPolicy",
        "PolicyDocument" : {
          "Statement" : [
            {
              "Effect"   : "Allow",
              "Action"   : [ "sts:GetFederationToken" ],
              "Resource" : [ "*" ]
            },
            {
              "Effect"   : "Allow",
              "Action"   : [ "s3:PutObject" ],
              "Resource" : [ "arn:aws:s3:::*" ]
            }
          ]
        }
      }
    },

    "PublisherKeys" : {
      "Type"       : "AWS::IAM::AccessKey",
      "Properties" : { "UserName" : { "Ref": "Publisher" } }
    }
  },

  "Outputs" : {
    "S3BucketName" : {
      "Description" : "Name of S3 bucket",
      "Value"       : { "Ref" : "S3PrivateBucket" }
    },
    "AccessKey" : {
      "Description" : "AWSAccessKeyId of new user",
      "Value"       : { "Ref" : "PublisherKeys" }
    },
    "SecretKey" : {
      "Description" : "AWSSecretKey of new user",
      "Value"       : { "Fn::GetAtt" : ["PublisherKeys", "SecretAccessKey"]}
    }
  }
}
```

### 補足
IAMユーザー（Publisher）にもS3バケットへアップロード可能な権限が必要。
言い換えると、発行するトークンには発行者以上の操作権限を付与できない。
トークンの発行時にはエラーにならないので、ハマりやすいポイント。

### 実行結果
![Screenshot_from_2014-06-05 11:17:40.png](https://qiita-image-store.s3.amazonaws.com/0/24703/a6826d36-cde8-1d83-998c-11e88a784284.png)

## 投稿

実際に投稿確認してみる。

### サンプルコード

```rb:sample.rb
require 'logger'
require 'aws'

(file_path, bucket_name, object_key_prefix) = ARGV
unless file_path && bucket_name && object_key_prefix
  puts "Usage: #{File.basename(__FILE__)} <FILE_PATH> <BUCKET_NAME> <OBJECT_KEY_PREFIX>"
  exit 1
end
raise "File(#{file_path}) not found" unless File.exist?(file_path)

username      = "TemporaryUser"
duration      = 900
s3_arn        = "arn:aws:s3:::#{bucket_name}/#{object_key_prefix}/*"
s3_object_key = File.basename(file_path)

AWS.config(logger: Logger.new($stdout))

upload_policy  = AWS::STS::Policy.new {|policy| policy.allow( actions: ['s3:PutObject'], resources: s3_arn) }
upload_session = AWS::STS.new.new_federated_session(username, policy: upload_policy, duration: duration)

s3     = AWS::S3.new(upload_session.credentials.select {|k,v| [:access_key_id, :secret_access_key, :session_token].include?(k) })
bucket = s3.buckets[bucket_name]
raise 'Bucket not exists' unless bucket.exists?

# success
s3_object_key = "#{object_key_prefix}/" + File.basename(file_path)
bucket.objects[s3_object_key].write(file: file_path)

# deny path
s3_object_key = "deny_#{object_key_prefix}/" + File.basename(file_path)
bucket.objects[s3_object_key].write(file: file_path)
```

### 実行結果

```bash
$ ruby sample.rb upload_file s3bukect_name object_key_prefix
I, [2014-06-05T02:38:33.113917 #18497]  INFO -- : [AWS STS 200 1.202314 0 retries] get_federation_token(:duration_seconds=>900,:name=>"TemporaryUser",:policy=>"{\"Version\":\"2008-10-17\",\"Statement\":[{\"Sid\":\"****\",\"Effect\":\"Allow\",\"Resource\":[\"arn:aws:s3:::s3bukect_name/object_key_prefix/*\"],\"Action\":[\"s3:PutObject\"]}]}")  

I, [2014-06-05T02:38:34.012440 #18497]  INFO -- : [AWS S3 200 0.149033 0 retries] put_object(:bucket_name=>"s3bukect_name",:content_length=>0,:data=>#<File:upload_file (0 bytes)>,:key=>"object_key_prefix/upload_file")  

I, [2014-06-05T02:38:34.147673 #18497]  INFO -- : [AWS S3 403 0.134181 0 retries] put_object(:bucket_name=>"s3bukect_name",:content_length=>0,:data=>#<File:upload_file (0 bytes)>,:key=>"deny_object_key_prefix/upload_file") AWS::S3::Errors::AccessDenied Access Denied
```