twingo_b's blog

memo

IAM Roles for EC2 instancesを利用して、S3 Pre-signed URL生成/Direct Object Upload

AWS Advent Calendar 2012の9日目です。

IAM roles for EC2 instances(以下IAM roles)ってご存知でしょうか? AWSを利用して開発していると、どうしてもAccessKeyId/SecretAccessKeyの扱いに困りますよね?

IAM rolesを利用すると、このようにプログラムやコンフィグファイルに埋め込んでいたものが、

1
2
3
4
5
require_once 'AWSSDKforPHP/sdk.class.php';
$s3 = new AmazonS3(array(
  'key' => 'xxxxxx',
  'secret' => 'xxxxxx',
));

書かなくて良くなります^^

1
2
3
4
require_once 'AWSSDKforPHP/sdk.class.php';
$s3 = new AmazonS3(array(
  'default_cache_config' => '/tmp'
));

今回は、普段良く利用しているS3 Pre-signed URL生成/Direct Object UploadがIAM rolesでもちゃんと動くか検証しないとなーと前から思っていましたので、この機会にやってみることにしました。

前提条件

以下の環境で動作確認を行いました。

  • Amazon Linux AMI 2012.09
  • PHP 5.3.18
  • php-amazon-sdk-1.5.17

IAM rolesの作成

詳細は、クラスメソッド開発ブログさんのIAM roles for EC2 instancesって何?を参照されると良いと思います。今回S3を利用する上でポイントとなる部分を説明します。

特定のバケット以下のみアクセス権を与えたいので、Edit Permissionsで、下記のように設定しました。

特定のバケットのみ操作を許可したい場合でも、ListAllMyBucketsはすべて許可しておく必要があります。get_bucket_list()が失敗しますので。また、特定のバケット以下のみ許可する場合に、arn:aws:s3:::[bucket]arn:aws:s3:::[bucket]/*を両方書かないと、うまく動かなかったです^^;

生成されたjsonはこちらになります。

EC2インスタンスの起動

ここもポイントだけ。Launch Instance時はClassic Wizardを選ばないと、IAM Roleを選べなかったです。

セットアップ

最低限、必要なものだけインストールしました。

1
sudo yum -y install php httpd php-amazon-sdk

S3 Pre-signed URL生成

ここからが本題です。

サンプルコードはこちらです。とってもシンプルですね。

実行すると、このように1分のみ有効なURLが生成されます。 プログラム埋め込み時は固定だったAWSAccessKeyIdも、定期的に変更されます^^

1
2
php get_object_url.php 
https://test-twingob.s3.amazonaws.com/test.txt?AWSAccessKeyId=ASIAJQ4QZDDBS22LYMGA&Expires=1355056156&Signature=Zq9j1K6eG9zDjzA7uUrgIRr2i10%3D&x-amz-security-token=AQoDYXdzEC0agALr%2BzwOiErKaI1Jd1kg7Ejc1zx5TpExGmaYG2VliSjiOX%2BHZ09j9397tnGlcKthKixDTUDf49tHssOxAuyLAK5vM5jjz%2BA6CM4jZUz43%2FN4BbMWI%2FnyN2Bdf96xGrkOp1puOqoXt0hk134%2BqUEySXtSFIds8S%2B4Ijn0Oa7hlaVykkhfcx6YErTOfS6mc6U3DjkxZB9P9ia6c%2FlGEFexUzNVYi1hvgxTshBHA8bi%2FMXqhLNeY5XoDJ13S4Ym8BxD0N2P0thlEMF80xb9WcRyhuFWn4xumUSbdWYAcvXbrKR9mwbeY0qeRRUzBBAwSM%2BJM4rH8X5tLQQs4OCC7bCVx0EJIJvpkYYF

Direct Object Upload

CDPのDirect Object Uploadパターンになります。PHPで簡単に実現する方法ないかなーと思って探していたら、S3BrowserUploadを見つけました!自分でpolicyやsignature生成しなくても良いので、とっても便利!でも落とし穴が^^;

IAM rolesでAPIを利用するときに必要なx-amz-security-tokenを自動で付与してくれないんですよね。付与しない状態で動かすと、

1
InvalidAccessKeyId

とエラーが出力されて、アップロードに失敗します。かなりググって悩みました。

サンプルコードはこちらです。x-amz-security-tokenが解決できれば、すごくシンプルに書くことができました。

実行すると、このようなHTMLが生成されます。このフォームも表示後1分のみ有効です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
curl http://localhost/post_object.php
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  </head>
  <body>
    <form action="http://test-twingob.s3.amazonaws.com/"
          method="POST"
          enctype="multipart/form-data">
    
                <input type="hidden" name="AWSAccessKeyId" value="ASIAJQ4QZDDBS22LYMGA">
                <input type="hidden" name="success_action_status" value="201">
                <input type="hidden" name="acl" value="private">
                <input type="hidden" name="Content-Disposition" value="attachment; filename=00000001.txt">
                <input type="hidden" name="Content-Type" value="text/">
                <input type="hidden" name="key" value="00000001.txt">
                <input type="hidden" name="success_action_redirect" value="http://twingo-b.github.com/?id=00000001">
                <input type="hidden" name="x-amz-security-token" value="AQoDYXdzEC0agALr+zwOiErKaI1Jd1kg7Ejc1zx5TpExGmaYG2VliSjiOX+HZ09j9397tnGlcKthKixDTUDf49tHssOxAuyLAK5vM5jjz+A6CM4jZUz43/N4BbMWI/nyN2Bdf96xGrkOp1puOqoXt0hk134+qUEySXtSFIds8S+4Ijn0Oa7hlaVykkhfcx6YErTOfS6mc6U3DjkxZB9P9ia6c/lGEFexUzNVYi1hvgxTshBHA8bi/MXqhLNeY5XoDJ13S4Ym8BxD0N2P0thlEMF80xb9WcRyhuFWn4xumUSbdWYAcvXbrKR9mwbeY0qeRRUzBBAwSM+JM4rH8X5tLQQs4OCC7bCVx0EJIJvpkYYF">
                <input type="hidden" name="policy" value="eyJjb25kaXRpb25zIjpbeyJidWNrZXQiOiJ0ZXN0LXR3aW5nb2IifSx7InN1Y2Nlc3NfYWN0aW9uX3N0YXR1cyI6IjIwMSJ9LHsiYWNsIjoicHJpdmF0ZSJ9LHsiQ29udGVudC1EaXNwb3NpdGlvbiI6ImF0dGFjaG1lbnQ7IGZpbGVuYW1lPTAwMDAwMDAxLnR4dCJ9LFsic3RhcnRzLXdpdGgiLCIkQ29udGVudC1UeXBlIiwidGV4dFwvIl0seyJrZXkiOiIwMDAwMDAwMS50eHQifSx7InN1Y2Nlc3NfYWN0aW9uX3JlZGlyZWN0IjoiaHR0cDpcL1wvdHdpbmdvLWIuZ2l0aHViLmNvbVwvP2lkPTAwMDAwMDAxIn0seyJ4LWFtei1zZWN1cml0eS10b2tlbiI6IkFRb0RZWGR6RUMwYWdBTHIrendPaUVyS2FJMUpkMWtnN0VqYzF6eDVUcEV4R21hWUcyVmxpU2ppT1grSFowOWo5Mzk3dG5HbGNLdGhLaXhEVFVEZjQ5dEhzc094QXV5TEFLNXZNNWpqeitBNkNNNGpaVXo0M1wvTjRCYk1XSVwvbnlOMkJkZjk2eEdya09wMXB1T3FvWHQwaGsxMzQrcVVFeVNYdFNGSWRzOFMrNElqbjBPYTdobGFWeWtraGZjeDZZRXJUT2ZTNm1jNlUzRGpreFpCOVA5aWE2Y1wvbEdFRmV4VXpOVllpMWh2Z3hUc2hCSEE4YmlcL01YcWhMTmVZNVhvREoxM1M0WW04QnhEME4yUDB0aGxFTUY4MHhiOVdjUnlodUZXbjR4dW1VU2JkV1lBY3ZYYnJLUjltd2JlWTBxZVJSVXpCQkF3U00rSk00ckg4WDV0TFFRczRPQ0M3YkNWeDBFSklKdnBrWVlGIn1dLCJleHBpcmF0aW9uIjoiMjAxMi0xMi0wOVQxMjo1Njo1OVoifQ==">
                <input type="hidden" name="signature" value="xjyu5PRSm0mZLg5s6yvbH4FUY9I=">
            
        <input type="file" name="file">
        <input type="submit" name="upload" value="Upload">
    </form>
  </body>
</html>

さいごに

ちゃんと動いてよかった^^。動かなくてS3BrowserUploadのgithubのソースコード見てたら、authorがラスベガスのre:Inventでお会いしたJeremyさんでした。ちょっとほっこりした^^