CloudFrontを経由してS3の静的ウェブサイトホスティングの機能を使う

  • 11
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Amazon S3をWebサーバとして利用できる、静的ウェブサイトホスティングの機能を利用してみました。
また、今回はCloudFrontを経由させることで、ELBとS3へそれぞれリクエストを振り分ける、ということも合わせて試しました。
(CloudFrontのキャッシュ機能の話については今回の対象外となります。今回はリクエストの振り分け用途として利用しています)

イメージとしては以下のようになります。

また、S3へのファイルアップロードは、EC2から行う想定です。

ELB

事前に作成して設定しておきます。
作成方法は公式ドキュメント (http://docs.aws.amazon.com/ja_jp/ElasticLoadBalancing/latest/DeveloperGuide/elb-getting-started.html) を参照してください。

また、CloudFront経由でのアクセスになるため、セキュリティグループでInboundへの任意の場所からのHTTP 80番ポートを許可しておきます。

EC2

事前にEC2を作成しておき、Apache httpdをインストールした後、コンテンツを配置します。
作成方法については割愛します。
今回はデフォルトのテストページをそのまま利用します。

また、このコンテンツをインターネットに公開したくない場合、ELB=>EC2の場合はセキュリティグループでIPアドレス制限を設定することが多いと思いますが、CloudFrontは今日時点ではアクセス制限を掛けることが出来ません。
そのため、EC2のWebサーバ側でアクセス制限を設定します。(S3へのアクセスはこの方法だと制限出来ませんが)

以下はApache httpd2.4での最低限の設定例になります。CloudFront, ELBを経由するため、X-Forwarded-Forにリクエスト元のIPアドレスが入ります。
(詳細は要件に合わせて設定します。)

RemoteIPHeader X-Forwarded-For

<Location />
  Require all denied
  Require ip IPアドレス/32
</Location>

S3

バケットの作成

公式ドキュメント (http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/UG/CreatingaBucket.html) などを参照し、バケットを作成しておきます。

バケットポリシーの追加

ポリシージェネレーターを利用して、バケットポリシーを作成します。
(http://awspolicygen.s3.amazonaws.com/policygen.html)

{
  "Id": "Policyxxxxxxxxxxxxx",
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Stmtxxxxxxxxxxxxx",
      "Action": [
        "s3:GetObject"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::バケット名/*",
      "Principal": "*"
    }
  ]
}

静的ウェブサイトホスティングの設定

S3の静的ウェブサイトホスティングの設定を行います。

  • インデックスドキュメント:必須設定となります。設定したいファイルがある場合は、設定したいファイル名を指定します。今回は特定のパスかつ特定のファイル名の場合のみS3から返すことを想定し、実在しないファイル名を指定しました。
  • エラードキュメント:必要に応じて設定します。
  • リダイレクト:必要に応じて設定します。今回は、インデックスドキュメントのindex.htmlは存在しないため、404になってしまった場合はELBにリダイレクトする、という形を試しました。

Kobito.sd4n2Y.png

  • リダイレクトルール例
<RoutingRules>
    <RoutingRule>
        <Condition>
            <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
        </Condition>
        <Redirect>
            <HostName>ELB名.ap-northeast-1.elb.amazonaws.com</HostName>
            <ReplaceKeyWith></ReplaceKeyWith>
        </Redirect>
    </RoutingRule>
</RoutingRules>

CloudFront

Distributionの設定

公式ドキュメント等を参照し、Distibutionの設定を行います。
設定後は以下のようになります。

Kobito.5riopm.png

Originの設定

Originの設定を行います。ELBとS3の2つ分を設定します。
設定後は以下のようになります。

S3向けの設定の注意点として、S3に設定したリダイレクト設定を効かせたい場合、Origin Domain Nameにはドロップダウンリストに表示されるS3バケット名ではなく、S3のEndpoint名を直接指定します。
(そうしないとリダイレクトが効かなかった)

Kobito.GFb5EH.png

Behaviorの設定

Behaviorの設定にて、振り分けるパスの設定を行います。
今回は、/test/* の場合はS3、それ以外はELBへ振り分けを行うようにしています。

Kobito.VEakPe.png

IAM設定

EC2からS3にファイルをアップロード出来るようにするため、EC2に割り当てているIAMロールにS3へのポリシーをアタッチします。

AWS Management Consoleから行う場合、IAM - ポリシーの作成 - Policy Generator と進み、必要なアクションを指定してポリシーを作成します。

Kobito.7JLRet.png

ここで必要分だけアクションを指定するのですが、とりあえずs3:DeleteObject、s3:GetObject、s3:PutObject、s3:RestoreObjectあたりを設定してEC2からAWS CLIにてlsを試したところ、エラーとなりました。

$ aws s3 ls s3://バケット名/
A client error (AccessDenied) occurred when calling the ListObjects operation: Access Denied

DEBUGしてみたところ、確かに403になっています。ListObjectという文言もありますが、S3のアクションではListObjectというのはありません。

$ aws s3 ls s3://バケット名 --debug
(snip)
MainThread - botocore.vendored.requests.packages.urllib3.connectionpool - INFO - Starting new HTTPS connection (1): バケット名.s3-ap-northeast-1.amazonaws.com
MainThread - botocore.vendored.requests.packages.urllib3.connectionpool - DEBUG - "GET /?prefix=&delimiter=%2F HTTP/1.1" 403 None
MainThread - botocore.hooks - DEBUG - Event after-call.s3.ListObjects: calling handler <function enhance_error_msg at 0x7fe82cb3e488>
(スタックトレース等が続く)

色々試行錯誤の結果、以下のようになりました。
その後、AWS CLIにてlsやrmが実行可能なことを確認しました。
細かい要件が決まっていない場合は、S3FullAccessとかでも良いかもしれません。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmtxxxxxxxxxxxxx",
            "Effect": "Allow",
            "Action": [
                "s3:DeleteObject",
                "s3:DeleteObjectVersion",
                "s3:GetLifecycleConfiguration",
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:GetObjectTorrent",
                "s3:GetObjectVersion",
                "s3:GetObjectVersionAcl",
                "s3:GetObjectVersionTorrent",
                "s3:ListAllMyBuckets",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads",
                "s3:ListBucketVersions",
                "s3:ListMultipartUploadParts",
                "s3:PutLifecycleConfiguration",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "s3:PutObjectVersionAcl",
                "s3:RestoreObject"
            ],
            "Resource": [
                "arn:aws:s3:::バケット名",
                "arn:aws:s3:::バケット名/*"
            ]
        }
    ]
}

最終的には以下にファイルを配置しました。

$ aws s3 ls s3://バケット名/test/
2015-12-19 14:34:06          0
2015-12-19 14:34:34          8 test.html

Distributionを有効化

全て設定が終わったら、CloudFrontのDistributionを有効化します。
StateがEnabledに変化しています。

Kobito.oF9uKj.png

ブラウザからアクセス

それでは、この状態で、CloudFront経由でアクセスしてみます。
パスごとに振り分け出来ていることが確認出来ました。

→EC2上のコンテンツが表示される

Kobito.eFPQwD.png

→自分でS3に配置したコンテンツが表示される