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

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

目次

    はじめに

    クラウドインテグレーション部の渡邊です。
    今回は、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 で取得する

    アジアクエスト株式会社では一緒に働いていただける方を募集しています。
    興味のある方は以下のURLを御覧ください。