6
3

More than 5 years have passed since last update.

CloudFrontのバックエンドを多段にしてS3を使う場合のアクセス制御

Posted at

概要

CloudFrontのバックエンドのElasticBeanstalkに構築したNginxを使い、そのNginxからproxy_passでprivateなS3を利用したときに、アクセス制御でハマったのでその解決方法について共有

構成

CloudFront -> ElasitcBeanstalk(nginx) -> S3(private)

詳細

S3がprivateなのでアクセスするには設定が必要で、以下の2つの方法を検討した。

  1. VPC Endpointを使用
  2. IAMでのアクセス制御

1の方法は今回利用したいアプリケーション以外にも同じVPCに属するインスタンスからのアクセスも許可してしまうデメリットがある。今回のケースではReadOnlyなアクセスなので、それでも副作用は特にないのだが、2の方がより適切なアクセス制御ができるので2を採用した。

nginx+luaでproxy_passするときに認証情報をheaderに付与した。該当処理部分の抜粋は以下のとおり。AWSのドキュメントはこちら: RestAuthentication

    location ~ ^/path/(.+)$ {
        set_by_lua_file $bucket '/usr/local/nginx/lua/env.lua' 'BUCKET';
        set_by_lua_file $access_key '/usr/local/nginx/lua/env.lua' 'AWS_ACCESS_KEY';
        set_by_lua_file $secret_key '/usr/local/nginx/lua/env.lua' 'AWS_SECRET_KEY';
        rewrite_by_lua '
            local date = ngx.http_time(ngx.time())
            local string_to_sign = ngx.req.get_method() .. "\\n\\n\\n" .. date .. "\\n/" .. ngx.var.bucket .. "/" .. ngx.var[1]
            ngx.req.set_header("Date", date)
            local digest = ngx.hmac_sha1(ngx.var.secret_key, string_to_sign)
            local signature = ngx.encode_base64(digest)
            ngx.req.set_header("Authorization", "AWS " .. ngx.var.access_key .. ":" .. signature)
            ngx.req.clear_header("x-amz-cf-id") -- ここでCFのHeaderを除去
        ';
        proxy_hide_header x-amz-id-2;
        proxy_hide_header x-amz-request-id;
        proxy_hide_header Cache-Control;
        proxy_set_header Host $bucket.s3.amazonaws.com;
        proxy_pass http://$bucket.s3.amazonaws.com/$1;
        error_page 415 /50x.html;
    }

使用するIAM Userに該当S3へのRead権を適切に付与してあれば、これで ElasticBeanstalk(nginx) -> S3(private) 間のアクセスは何の問題もないのだが、CloudFront経由でアクセスしたときには問題がおこる。

CloudFront -> ElasticBeanstalk(nginx) のアクセス時にCloudFrontはいくつかのHeaderを付与する(ex: User-Agent, x-amz-cf-id..)。x-amz-cf-id が付与されていると、S3はCloudFrontからのアクセスだと判断して、そのCloudFrontからのアクセスが許可されていないとIAMの正しいAuthorization HeaderがあってもDeny(403)になってしまうのだ。CloudFrontからS3へのアクセス設定はOriginAccessIdentityを用い、CloudFrontのdistributionで設定できる。ただし、CloudFrontのOriginがS3のときのみ設定できるもので、今回はCloudFrontの直接のバックエンドはElasticBeanstalkなので使用できない。そこで、nginxでproxy_passする際に、 x-amz-cf-id をHeaderから取り除くことでIAM認証を有効にすることで解決した。

マネージドサービスはメリットの方が大きいと思いますが、動きを詳細に全部理解して使うのも難しく、こういった予期せぬ、期待せぬ挙動にあうとハマってしまいがちだなと思うところです。

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3