ばーろぐわにる

SIerからWEB系?インフラエンジニアにジョブチェンジした見習いの備忘録

LmabdaからS3のファイルを読み込む

やりたいこと

LambdaでS3によってデフォルト暗号化されたファイルを読み込みたい。LambdaでS3上のデータを取得してガチャガチャするのに必要な処理。

テスト用S3バケット作成

まずはS3バケットの作成。今回はアップロードするファイルが機密性の高いものと想定して、デフォルト暗号化を有効にする。

デフォルト暗号化とは

アップロードしたファイルをS3側で暗号化して保存してくれる。クライアント側で暗号化する必要がないため、クライアント側の負荷が上がることはない。ただし、S3側で暗号化処理が実行される分のオーバーヘッドが発生する可能性は考慮する必要がある。

dev.classmethod.jp

docs.aws.amazon.com

バケット作成&ファイルアップロード

S3バケットを作成してテスト用のファイルを配置する。デフォルト暗号化の暗号鍵は SSE-S3 を利用。

f:id:oneal-desu:20181008165910p:plain
デフォルト暗号化はバケット作成時に設定。バケット作成後にも設定できるが、既にアップロードされたファイルは暗号化されないので注意。

f:id:oneal-desu:20181008172317p:plain
コンソールからアップロードする際、「暗号化なし」を選択しても自動的に暗号化される

Boto3を利用してS3のファイルを読み込む

Lambda関数作成

f:id:oneal-desu:20181008172552p:plain
「S3 オブジェクトの読み取り専用アクセス権限」を付与して関数を作成

テストスクリプト

import json
import boto3
import logging

client = boto3.client('s3')

def lambda_handler(event, context):

    # set logging
    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    # set "bucket name" and "object name"
    BUCKET = "waneal-test"
    TEST_FILE   = "hoge.txt"
    
    # get s3 object
    try:
        response = client.get_object(Bucket = BUCKET, Key = TEST_FILE)
    except Exception as e:
        return logger.error("Failed to get object: {}".format(e))
        
    return {
        "statusCode": 200,
        "body": "object body is {}".format(response['Body'].read().decode('utf-8'))
    }

Lambdaのログ出力は printlogger を利用できる。どちらもCloudwatchlogsに出力され、コンソールにも表示される。loggerを利用したほうがログレベル、タイムスタンプが含まれるため便利

docs.aws.amazon.com

存在しないファイルにアクセスすると Access Denied が返ってくる。少し紛らわしい。

response['Body']は文字列型ではなく botocore.response.StreamingBody なので、ストリームからの呼び出しが必要。一度読み込むと2度目以降は読み込めないので注意。これは意図された仕様で、理由は文字列型にしてしまうと、数GBのデータをLambda上のメモリで保持する可能性が出るため。

github.com

qiita.com

実行結果

f:id:oneal-desu:20181008200014p:plain

感想

ストリームのところの仕様が把握できてなくて少しハマった。ちゃんとドキュメントを読むべし。

Lambdaで暗号化したファイルを読み込む際、特に復号化処理は意識しなくてもいい。逆に言うと同じアカウント内であれば暗号化したファイルを利用可能なので、SSE-S3による暗号化で防げるのは バケットの公開設定を誤った場合 のみになるのかな?内部でアクセスを制限したい場合、KMSによる鍵管理やIAMユーザ権限管理が必要なんだと思う。