AQ Tech Blog

【小ネタ】管理者権限のEC2から同アカウントのバケットポリシーに制限をかけていないS3へのcurlリクエストに失敗するのはなぜか

作成者: tsuyoshi.watanabe|2024年03月22日

はじめに

クラウドインテグレーション部の渡邊です。
今回は、S3へのcurlリクエストについて考えます。
EC2からS3へリクエストをすることを想定すると、アクセス制限のまったくないS3バケットは外部からcurlなどを利用してオブジェクト情報を取得できます。
しかし、アクセス制限がある場合はIAMロールの認証情報などを含める必要があります。

まとめ

  • アクセス制限のあるS3へEC2からリクエストを送るためには以下に注意する
    • AWSのSignature Version 4 (SIGv4) をサポートしているcurlのパッケージを利用し、リクエストにSIGv4の署名を付与する
    • x-amz-content-sha256を含める(空文字のハッシュ値)

環境情報

  • Amazon Linux 2
  • curl 8.0.1 (EC2)
    curlコマンドで--aws-sigv4フラグを使えるようにするために、7.75以上のバージョンが必要です。

実際にやってみる

署名情報や必要なヘッダが含まれていないcurlのリクエスト

  • 前提
    • EC2にアタッチしたIAMロールには管理者権限を付与
    • 同アカウントに配置しているリクエスト先のS3へバケットポリシーによる制限をかけていない

アクセス先のS3へリクエストを送ると、オブジェクトの一覧を取得できます。

$ aws s3 ls watanabe-object-wp --recursive | grep -v '/$' | awk '{print $4}'

test.txt

中身を見るためにcurlによるオブジェクト情報の読み取りを試みます。
しかし、curlによるファイルの読み取りに失敗します。

$ curl -k -XGET "https://watanabe-object-wp.s3.amazonaws.com/test.txt"
<?xml version="1.0" encoding="UTF-8"?>

<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>SH31WFKGCWH91G00</RequestId><HostId>PeW/1mkgfDLHMFQo/7EAFRk5u7xN+R3AwKygfIDrZfwFOVRxm90VjwUTwP7TZoe8Uq/DgjCcB+Q=</HostId></Error>

なぜなら、リクエスト時にAWSの署名付与していないためです。
これでは、任意の外部のサーバからAWS S3にリクエストしているのと変わりません。
バケットポリシーやブロックパブリックアクセスの設定を変更して、外部からアクセスできるようにしない限り、リクエストは失敗します。

署名情報を含めたcurlのリクエスト

では次に署名情報を付与したリクエストをします。
まずは以下の環境情報を用意してください(リージョン情報などは適宜変更してください)

TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`
ROLE_NAME=`curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/iam/security-credentials/`
CRED=`curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/${ROLE_NAME}/`
AWS_ACCESS_KEY_ID=`echo $CRED | jq -r ".AccessKeyId"`
AWS_SECRET_ACCESS_KEY=`echo $CRED | jq -r ".SecretAccessKey"`
AWS_SESSION_TOKEN=`echo $CRED | jq -r ".Token"`
REGION="us-east-1"
SERVICE="s3"

では、リクエストを実行します。
リクエストに含むオブジェクトのURLはAWSのコンソール画面などから取得が可能です。

$ curl -k -XGET "https://watanabe-object-wp.s3.amazonaws.com/test.txt" -H "X-Amz-Security-Token: ${AWS_SESSION_TOKEN}" --aws-sigv4 "aws:amz:${REGION}:${SERVICE}" --user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}"

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>InvalidRequest</Code><Message>Missing required header for this request: x-amz-content-sha256</Message><RequestId>RFK35PPH28X1NEVV</RequestId><HostId>Ni7j03DDfJVDhYZ8ctSedjs/YG205LucpRfNhMNm2YZJUMe+FQJRw1KRszaAOUka+Slut57e7sM=</HostId></Error>

必要なヘッダがないことが原因で、リクエストに失敗しています。

署名情報や必要なヘッダを含めたcurlのリクエスト

前項のリクエストにx-amz-content-sha256ヘッダを含める必要があります。
値として、空文字のハッシュ値を表すe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855を指定すると、リクエストに成功しました。

$ curl -k -XGET "https://watanabe-object-wp.s3.amazonaws.com/test.txt" -H "X-Amz-Security-Token: ${AWS_SESSION_TOKEN}" -H "x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" --aws-sigv4 "aws:amz:${REGION}:${SERVICE}" --user "${AWS_ACCESS_KEY_ID}:${AWS_SECRET_ACCESS_KEY}"

test


余談

ブロックパブリックアクセスをオフにして、バケットポリシーを以下のようにすると外部からもアクセスできるようになり、署名情報や必要なヘッダが含まれていないcurlのリクエストに成功します。

バケットポリシー

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::watanabe-object-wp/*"
}
]
}

リクエスト

$ curl -k -XGET "https://watanabe-object-wp.s3.amazonaws.com/test.txt"

test


【参考】
 S3 オブジェクトの一覧をあまり手間をかけずに AWS CLI で取得する