のぴぴのメモ

自分用のLinuxとかの技術メモ

Pythonで署名付きURLを作成し S3オブジェクトをGET/PUTしてみた

はじめに

この記事は、S3の署名付きURLの学習のために署名付きURLを利用してS3のオブジェクトをPUT/GETしたときの手順をまとめたものです。
ポイントは以下のとおりです。

  • S3の署名付きURLは、AWS CLISDK、マネージメントコンソールで生成可能
  • ただしAWS CLIでは、オブジェクトのPUT(アップロード)用の署名付きURLは不可
  • boto3でS3の署名付きURLを生成する場合は、generate_presigned_urlで生成可能(引数でPUT用/GET用を指定)

手順

環境準備

プロファイル設定
#プロファイルとリージョン設定
PROFILE="<利用したいプロファイル名:デフォルトの場合はdefaultを指定>"
REGION="<実行したいリージョン:ap-northeast-1など>"

aws --profile ${PROFILE} --region ${REGION} sts get-caller-identity
S3バケット作成
#S3バケット作成
GET_BUCKET="xxxxx-get-bucket"
PUT_BUCKET="xxxxx-put-bucket"
OBJECT="hello-world.txt"


aws --profile ${PROFILE} --region ${REGION} s3api create-bucket --bucket "${GET_BUCKET}" --create-bucket-configuration "LocationConstraint=${REGION}"
aws --profile ${PROFILE} --region ${REGION} s3api create-bucket --bucket "${PUT_BUCKET}" --create-bucket-configuration "LocationConstraint=${REGION}"

#GET用バケットへのオブジェクト配置
echo 'Hello World!!' > ${OBJECT}
aws s3 --profile ${PROFILE} --region ${REGION} cp "${OBJECT}" "s3://${GET_BUCKET}"
aws s3 --profile ${PROFILE} --region ${REGION} ls "s3://${GET_BUCKET}"
EC2用のインスタンスロール作成
#S3アクセス用のインスタンスロール作成
#IAMロール作成
POLICY='{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}'
aws --profile ${PROFILE} \
    iam create-role \
        --role-name "EC2-PreSignS3Test" \
        --assume-role-policy-document "${POLICY}" \
        --max-session-duration 43200

# SSM用カスタマー管理ポリシーのアタッチ(SSMでインスタンスログインするためで検証自体としては不要)
aws --profile ${PROFILE} \
    iam attach-role-policy \
        --role-name "EC2-PreSignS3Test" \
        --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

#S3アクセス用のインラインポリシー追加
InlinePolicyDocument='{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ForGetBucket",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::'"${GET_BUCKET}"'",
        "arn:aws:s3:::'"${GET_BUCKET}"'/*"
      ]
    },
    {
      "Sid": "ForPutBucket",
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::'"${PUT_BUCKET}"'",
        "arn:aws:s3:::'"${PUT_BUCKET}"'/*"
      ]
    }
  ]
}'
aws --profile ${PROFILE} \
    iam put-role-policy \
        --role-name "EC2-PreSignS3Test" \
        --policy-name "AllowS3Bucket" \
        --policy-document "${InlinePolicyDocument}"

#インスタンスプロファイルの作成
aws --profile ${PROFILE} \
    iam create-instance-profile \
        --instance-profile-name "EC2-PreSignS3Test";

aws --profile ${PROFILE} \
    iam add-role-to-instance-profile \
        --instance-profile-name "EC2-PreSignS3Test" \
        --role-name "EC2-PreSignS3Test" ;

|

EC2用インスタンス作成
PubSubnetID="<インスタンスを作成するパブリックサブネットのID: subnet-xxxx>"
SgId="<インスタンスに付与するサブネットID>"
KEYNAME="<SSH接続する場合はキーペア指定>"

#最新のAmazon Linux2のAMI IDを取得します。
AL2_AMIID=$(aws --profile ${PROFILE} --region ${REGION} --output text \
    ec2 describe-images \
        --owners amazon \
        --filters 'Name=name,Values=amzn2-ami-hvm-2.0.????????.?-x86_64-gp2' \
                  'Name=state,Values=available' \
        --query 'reverse(sort_by(Images, &CreationDate))[:1].ImageId' ) ;
echo -e "PubSubnetID = ${PubSubnetID}\nSgId        = ${SgId}\nKEYNAME     = ${KEYNAME}\nAL2_AMIID   = ${AL2_AMIID}"

#インスタンスの作成
#インスタンスタイプ設定
INSTANCE_TYPE="t2.micro"

#タグ設定
TAGJSON='
[
    {
        "ResourceType": "instance",
        "Tags": [
            {
                "Key": "Name",
                "Value": "PreSignS3Test"
            }
        ]
    }
]'

# サーバの起動
aws --profile ${PROFILE} --region ${REGION} \
    ec2 run-instances \
        --image-id ${AL2_AMIID} \
        --instance-type "t2.micro" \
        --subnet-id ${PubSubnetID} \
        --security-group-ids ${SgId} \
        --associate-public-ip-address \
        --tag-specifications "${TAGJSON}" \
        --iam-instance-profile "Name=EC2-PreSignS3Test" \
        --key-name ${KEYNAME} ;  #SSH接続しない場合はこの行を削除する
インスタンス上でのAWS CLI設定
  • 作成したインスタンスにSSMまたはSSHで接続する
  • 下記コマンドを実行しAWS CLI環境をセットアップする
#AWS CLI環境の設定
REGION=$(TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \
              && curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//')
aws configure set region ${REGION}
aws configure set output json

#接続確認
aws sts get-caller-identity

S3署名付きURLでのオブジェクト取得(GET)

AWS CLIでのオブジェクト取得確認
#S3バケット情報の設定
GET_BUCKET="xxxxx-get-bucket"
OBJECT="hello-world.txt"

#AWS CLIでのオブジェクト確認(ファイル一覧にオブジェクトが表示されること)
aws s3 ls "s3://${GET_BUCKET}"

#AWS CLIでのオブジェクト取得確認
aws s3 cp "s3://${GET_BUCKET}/${OBJECT}" .
署名付きURLでのオブジェクト取得用のpythonスクリプト配置
  • python3とboto3のセットアップ
sudo yum -y install python3
sudo pip3 install boto3
  • get.pyというファイル名で以下のファイルを格納
#!/usr/bin/env python3
import sys
import boto3

#引数からバケットとオブジェクト情報を取得
BUCKET_NAME = sys.argv[1]
OBJECT_KEY  = sys.argv[2]
EXPIRESIN   = sys.argv[3]

url = boto3.client('s3').generate_presigned_url(
    ClientMethod='get_object',
    Params={'Bucket':  BUCKET_NAME, 'Key': OBJECT_KEY },
    ExpiresIn=EXPIRESIN)

print(url)
  • 以下のコマンドを実行して署名付きURLを生成
python3 get.py ${GET_BUCKET} ${OBJECT} 60
署名付きURLでのオブジェクトアップロードのpythonスクリプト配置

|

  • put.pyというファイル名で以下のファイルを格納
#!/usr/bin/env python3
import sys
import boto3

#引数からバケットとオブジェクト情報を取得
BUCKET_NAME = sys.argv[1]
OBJECT_KEY  = sys.argv[2]
EXPIRESIN   = sys.argv[3]

url = boto3.client('s3').generate_presigned_url(
    ClientMethod='put_object',
    Params={'Bucket':  BUCKET_NAME, 'Key': OBJECT_KEY },
    ExpiresIn=EXPIRESIN)

print(url)
python3 put.py ${PUT_BUCKET} ${OBJECT} 60
curl -X PUT --upload-file <ファイルパス> "<取得したPre-Signed URL>"
  • 補足事項
    • 上記検証でAmazon S3 から 307 Temporary Redirect レスポンスが返される場合: 作成したS3バケットバケット名がAWSの全リージョンに伝搬し切れてない場合に返されます。バケット名が伝搬するまでしばらく待ってから再実行して下さい。*1