1. はじめに
Python のプログラムコードから(S3 や CloudFront などの)AWS のサービスを利用する場合には、Boto(あるいは次期バージョンの Boto3)を使います。
Boto
boto: A Python interface to Amazon Web Services — boto v2.38.0
Boto3
AWS SDK for Python | アマゾン ウェブ サービス(AWS 日本語)
今回は、Boto を使って、S3上に格納されたオブジェクト(ファイル)を取得する方法について書いておきたいと思います。
なお、S3 へのアクセスは、特定の IAMユーザだけに GetObject/ListBucket を許可するように Bucket Policy でアクセス制限を行います。
ところでいつも悩むのですが、「Boto」の発音って 「ボトゥー」 でいいのでしょうか?? あるいは「ブートゥー」?「ボトー」?「ボートー」? いろいろググったのですが、結局分かりませんでした。どなたか詳しい方、教えてくださいまし。
2. IAMユーザを作成
アクセスキー(アクセスキーID およびシークレットアクセスキー)は、AWSルートアカウントで作成してしまうと漏洩してしまったときのインパクトが大きすぎるので、ルートアカウントは極力利用しないという方針のもと、「Admin」という名前の管理者用ユーザを作成して、そのユーザに対してアクセスキーを発行することとします。
以降、ルートアカウントでの操作です。
IAM のダッシュボードから、[Users] -> [Create New Users] を選択し、ユーザ名「Admin」を入力して「Create」ボタンをクリックし、新規の IAMユーザーを作成します。
Users 一覧からユーザ名「Admin」を選択して、ユーザの詳細情報ページに移動します。
まずは、「User ARN」を確認しておきます。この User ARN (Amazon Resource Name) は、S3 の Bucket Policy でアクセスを許可する IAMユーザを特定するために使用します。
ちなみに、IAM の User ARN の形式は以下の通りです。
arn:aws:{service}:{region}:{account}:{resource} {service} : IAMリソースの場合は「iam」 {region} : IAMリソースの場合は空白 {account} : ハイフンなしの AWS Account ID(AWSアカウントに対して1つ割り当てられる12桁の数字) {resouce} : IAMユーザやロールなどで識別する部分(今回の場合は「user/Admin」)
参考
IAM ID - AWS Identity and Access Management
次に、
[Security Credentials] -> [Access Keys] -> [Create Access Key] をクリックして、アクセスキーを作成します。このアクセスキーは、Boto のプログラム内で S3 への認証・認可を行うのに使用します。
ここで、Adminユーザに AWS 管理ポリシーをアタッチして、完全な管理者アクセス権限(AdministratorAccess)を与えます。*1
実際のポリシーはこのようになります。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "*", "Resource": "*" } ] }
最後に、
[Security Credentials] -> [Sign-In Credentials] -> [Manage Password] をクリックして、IAMユーザにパスワードを付けます。
一旦ログアウトし、
https://{AWS Account ID}.signin.aws.amazon.com/console
から、Adminユーザの新パスワードでログインできるかを確認しておきます。
なお、「AWS Account ID」は、AWSアカウントに紐づく 12桁の数字で、My Account ページの [Accout Settings] からも確認できます。
3. Bucket Policy を設定
S3 の Bucket Policy に、Adminユーザに対して ListBucket および GetObject を許可するポリシーを追加します。
Bucket Policy の構成要素については、以下のサイトで非常に分かりやすく説明されています。中でも、「Principal」「Resource」「Action」の書き方がポイントになります。
構成要素
誰(Principal)が、とある条件下(Condition)で、リソースに対し(Resource)、操作(Action)を行うことを制限(Effect)可能。
要素 説明 Version アクセスポリシーのバージョン。現在は唯一 ”2008-10-17” のみ有効 Id ポリシーオプションの識別子。UUID を使用、または組み込むことを推奨 Statement 下記の個々の要素を配列で持つ。JSON のブロックで構成 Sid Statement 内で一意に決まるサブID Effect statement が許可、または拒否かどうかを指定する必須の要素。有効値:"Allow", “Deny” Principal 設定したポリシーを受ける対象 Action 操作を許可する S3 の API について指定(例:s3:GetObject ) NotAction 唯一の操作を許可する S3 の API について指定。指定した API 以外は拒否 Resource ポリシーを指定するオブジェクトを指定 Condition 要素の内部について指定できる詳細な条件
「Policies · hamajyotan/castoro-s3-adapter Wiki · GitHub」より引用
その他の参考サイト
- 【基本おさらい】Amazon S3バケットに読み取り専用のポリシーをつけたAWS IAMポリシーをサクッと作る方法 | Developers.IO
- ポリシーでのプリンシパルの指定 - Amazon Simple Storage Service
- AWS リソースの管理に関するポリシーの例 - AWS Identity and Access Management
Bucket Policy をいちいち手で書くのは面倒なので、AWS Policy Generator を使って作成します。
http://awspolicygen.s3.amazonaws.com/policygen.html
なお、S3 の Resource は、「arn:aws:s3:::{S3バケット名}」という形式で指定可能です。
ListBucket の Policy
{ "Id": "Policy1439041728317", "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1439041726404", "Action": [ "s3:ListBucket" ], "Effect": "Allow", "Resource": "arn:aws:s3:::app1-transcoder-out", "Principal": { "AWS": [ "arn:aws:iam::xxxxxxxxxxxx:user/Admin" ] } } ] }
GetObject の Policy
{ "Id": "Policy1439040499430", "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1439040494019", "Action": [ "s3:GetObject" ], "Effect": "Allow", "Resource": "arn:aws:s3:::app1-transcoder-out/*", "Principal": { "AWS": [ "arn:aws:iam::xxxxxxxxxxxx:user/Admin" ] } } ] }
Statement の中のブロックをそれぞれコピペします。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Stmt1439041726404", "Action": [ "s3:ListBucket" ], "Effect": "Allow", "Resource": "arn:aws:s3:::app1-transcoder-out", "Principal": { "AWS": [ "arn:aws:iam::xxxxxxxxxxxx:user/Admin" ] } }, { "Sid": "Stmt1439040494019", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::xxxxxxxxxxxx:user/Admin" }, "Action": "s3:GetObject", "Resource": "arn:aws:s3:::app1-transcoder-out/*" } ] }
4. Boto のインストール
pip でインストールします。
$ pip install boto $ pip list | grep boto boto (2.38.0)
5. Boto を使った Python コード
「app1-transcoder-out」バケットが、
app1-transcoder-out/ └─HLS/ └─1M/ └─D0002021500_00000/ └─sample.m3u8
のようなディレクトリ構造になっていると仮定し、ファイル「sample.m3u8」をインメモリのファイルオブジェクトとして取得するソースコードを書いてみます。
test.py
#!/usr/bin/python from cStringIO import StringIO from boto import connect_s3 ACCESS_KEY_ID = '<YOUR ACCESS KEY ID>' SECRET_ACCESS_KEY = '<YOUR SECRET ACCESS KEY>' BUCKET_NAME = 'app1-transcoder-out' KEY_NAME = 'HLS/1M/D0002021500_00000/sample.m3u8' def main(): conn = connect_s3(ACCESS_KEY_ID, SECRET_ACCESS_KEY) bucket = conn.get_bucket(BUCKET_NAME) key = bucket.get_key(KEY_NAME) if key is None: raise Exception("No such key was found. key={}".format(key)) fp = StringIO() key.get_contents_to_file(fp) fp.seek(0) print fp.getvalue() fp.close() if __name__ == '__main__': main()
参考
実行結果
$ python test.py #EXTM3U #EXT-X-VERSION:3 #EXT-X-MEDIA-SEQUENCE:0 #EXT-X-ALLOW-CACHE:YES #EXT-X-TARGETDURATION:13 #EXTINF:12.078744, sample00000.ts #EXTINF:9.009011, sample00001.ts #EXTINF:9.009011, sample00002.ts #EXTINF:12.012011, sample00003.ts #EXTINF:9.009011, sample00004.ts #EXTINF:2.068733, sample00005.ts #EXT-X-ENDLIST
ちなみに、Bucket Policy の設定ミスなどでパーミッションエラーが発生した場合は、以下のような実行結果になります。
$ python test.py Traceback (most recent call last): File "test.py", line 33, in <module> main() File "test.py", line 18, in test bucket = conn.get_bucket(BUCKET_NAME) File "/opt/webapps/venv/local/lib/python2.7/site-packages/boto/s3/connection.py", line 502, in get_bucket return self.head_bucket(bucket_name, headers=headers) File "/opt/webapps/venv/local/lib/python2.7/site-packages/boto/s3/connection.py", line 535, in head_bucket raise err boto.exception.S3ResponseError: S3ResponseError: 403 Forbidden
まとめ
特定のIAMユーザにだけ Read Only でアクセスを許可した S3バケット内のオブジェクトを、Boto で取得する方法について書きました。Boto から S3への認証・認可は、アクセスを許可した IAMユーザのアクセスキー(アクセスキーID およびシークレットアクセスキー)を使います。
Bucket Policy の書き方には慣れが必要ですが、ACL で制限するよりも柔軟なアクセス制限を実現することができる上、ジェネレータやバリデータも充実しているため、より使いやすいという側面もあります。ただし、Bucket Policy で記述できる JSON のサイズに制限があるのが問題となるケースもレアながらあるようです。
*1: 今回の手順としては必須の設定ではありませんが、Adminユーザのアクセスキーを使用して AWS CLI を利用する場合などで必要となります。