3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Amazon S3の静的ウェブサイトホスティングをAmazon Route53のプライベートホストゾーンからエイリアスする

Last updated at Posted at 2020-08-17

はじめに

Amazon S3の静的ウェブサイトホスティングは便利。
でもエンドポイントがhttp://バケット名.s3-website-ap-northeast-1.amazonaws.comになってしまったりhttps://s3-ap-northeast-1.amazonaws.com/バケット名.comになってしまってイケてない。内部通信くらいはちゃんとしたサービス名をつけてあげたいけど内部通信のためにドメイン使用量は払いたくない。

そんなワガママなあなたに、良い感じの名前でアクセスするコツを書いておく。

さらに、今回の記事では、以下の構成で作った静的ウェブサイトホスティングに対してクロスアカウントでセキュアにアクセスする方法まで書いておく。

構成図_r2.png

Amazon Route53のプライベートホストゾーンを使おう

良い感じの名前を付けるのであれば、Amazon Route53のプライベートホストゾーンを使えば良い。

プライベートホストゾーンを設定する方法は以前の記事に書いている。

一つ注意しなければいけないのは、Amazon S3のエイリアスを作る場合は、「ホスト名=バケット名」になっている必要があるということだ。これをやっておかないと、Amazon Route53のホストゾーンでAレコードをS3に紐付けることができない。

Terraformで格納であればこんな感じになる。name = aws_s3_bucket.example.idとしているのがキモだ。
lifecycleについては後述する。

resource "aws_route53_zone" "example" {
  provider = aws.to_account

  name = local.zone_name

  vpc {
    vpc_id = data.aws_vpc.to.id
  }
}

resource "aws_route53_record" "example" {
  provider = aws.to_account

  zone_id = aws_route53_zone.example.zone_id
  name    = aws_s3_bucket.example.id
  type    = "A"

  alias {
    name                   = aws_s3_bucket.example.website_domain
    zone_id                = aws_s3_bucket.example.hosted_zone_id
    evaluate_target_health = true
  }
}

作ったエイリアスレコードを他のAWSアカウントと共有してクロスアカウントアクセスを可能にする

さて、せっかく作ったエイリアスレコードなのだから、複数のサービスで共通で使用したくなることがあるかもしれない。ないかもしれないしその可能性が高い。

だが、もしそういう事態が訪れた時には、AWS公式がやり方を紹介してくれている
このリンクお貼っておくだけでは芸がないので、Terraformで書くとこうなる、を置いておく。

resource "aws_route53_vpc_association_authorization" "example" {
  provider = aws.to_account

  vpc_id  = data.aws_vpc.from.id
  zone_id = aws_route53_zone.example.id
}

resource "aws_route53_zone_association" "example" {
  provider = aws.from_account

  vpc_id  = aws_route53_vpc_association_authorization.example.vpc_id
  zone_id = aws_route53_vpc_association_authorization.example.zone_id
}

これでterraform applyすると、aws_route53_zoneに指定したVPCが追加される。
しかし、VPCが追加されるということは、aws_route53_zone.exampleの.tfファイルとプロパティが合わなくなってしまうということだ。
すると、何が起こるかと言うと、次にapplyするタイミングで、実際のリソースとの差分を検知して、せっかく設定したVPCの設定が削除されてしまう。

これを避けるために、aws_route53_zone_associationでクロスアカウント設定する場合は、aws_route53_zoneに以下の設定を追加する。

resource "aws_route53_zone" "s3" {
  provider = aws.to_account

  name = local.zone_name

  vpc {
    vpc_id = data.aws_vpc.to.id
  }

+ lifecycle {
+   ignore_changes = [
+     vpc,
+   ]
+ }
}

これにより、vpcの変更は無視されるようになるため、次に関係のないリソースをterraform applyする際に差文検知しなくなる。

クロスアカウントアクセスをセキュアにする

さて、静的Webサイトホスティングの用途は千差万別だが、内部のためにクロスアカウントアクセスを設定したのであれば、以下のようにVPCエンドポイント経由のアクセスになるようルーティングをしておこう。

resource "aws_vpc_endpoint" "s3_gateway" {
  provider = aws.from_account

  vpc_id       = data.aws_vpc.from.id
  service_name = "com.amazonaws.ap-northeast-1.s3"
}

resource "aws_vpc_endpoint_route_table_association" "s3_gateway" {
  provider = aws.from_account

  route_table_id  = data.aws_route_table.from.id
  vpc_endpoint_id = aws_vpc_endpoint.s3.id
}

なお、コンテンツを一般公開するのであれば、バケットのパブリックアクセスを許容しないといけないが、VPCエンドポイント経由でプライベートアクセスする場合は、バケットポリシーでAllowするアクセス制御をしておけば、バケットの公開は不要だ。以下のように設定を行い、よりセキュアな状態にしておこう。

resource "aws_s3_bucket_public_access_block" "example" {
  provider = aws.to_account

  bucket = aws_s3_bucket.example.id

- block_public_acls       = false
+ block_public_acls       = true
- block_public_policy     = false
+ block_public_policy     = true
- ignore_public_acls      = false
+ ignore_public_acls      = true
- restrict_public_buckets = false
+ restrict_public_buckets = true
}

resource "aws_s3_bucket_policy" "example" {
  provider = aws.to_account

  bucket = aws_s3_bucket.example.id
  policy = data.aws_iam_policy_document.example_bucket_policy.json
}

data "aws_iam_policy_document" "example_bucket_policy" {
  statement {
    sid = "AllowVPCEndPointRead"

    effect = "Allow"

    principals {
      type        = "*"
      identifiers = ["*"]
    }

    actions = [
      "s3:GetObject",
    ]

    resources = [
      "arn:aws:s3:::${local.bucket_name}",
      "arn:aws:s3:::${local.bucket_name}/*",
    ]

+   condition {
+     test     = "StringEquals"
+     variable = "aws:sourceVpce"
+
+     values = [
+       "${aws_vpc_endpoint.s3_gateway.id}",
+     ]
+   }
+ }
}

おまけ) インターフェース型エンドポイントで固定IPアドレスを使ってS3へのプライベートアクセスを行う

通常はゲートウェイ型のVPCエンドポイントを使えばプライベートアクセスの要件を満たせるが、たとえばオンプレミスからのアクセスでDNS参照ができないためにIPアドレスを固定したいケースがたまにある。この場合、AWS公式のブログやAWS re:Postでも紹介されている通り、ENIをアタッチするインターフェース型エンドポイントを用いると良い。

インターフェース型エンドポイント(PrivateLink)でのS3へのアクセスはRESTAPIエンドポイント向けになるので、静的Webサイトホスティングは使用しない。この記事の趣旨とは多少異なるが、S3へのプライベート接続の一つの手法として紹介しておく。

設定はそんなに難しいことはなく、以下のようにインターフェース型のVPCエンドポイントを作成すれば良い。
セキュリティグループは80番ポートを解放しておくようにしよう。

resource "aws_vpc_endpoint" "s3_interface" {
  provider = aws.from_account

  vpc_id            = data.aws_vpc.from.id
  service_name      = "com.amazonaws.ap-northeast-1.s3"
  vpc_endpoint_type = "Interface"

  subnet_ids = data.aws_subnets.from.ids

  security_group_ids = [
    data.aws_security_group.from_http.id,
  ]

  private_dns_enabled = false
}

また、払い出されたエンドポイントIDをバケットポリシーにも追加するのを忘れないようにしよう。

data "aws_iam_policy_document" "example_bucket_policy" {
  statement {
    sid = "AllowVPCEndPointRead"

    effect = "Allow"

    principals {
      type        = "*"
      identifiers = ["*"]
    }

    actions = [
      "s3:GetObject",
    ]

    resources = [
      "arn:aws:s3:::${local.bucket_name}",
      "arn:aws:s3:::${local.bucket_name}/*",
    ]

    condition {
      test     = "StringEquals"
      variable = "aws:sourceVpce"

      values = [
        "${aws_vpc_endpoint.s3_gateway.id}",
+       "${aws_vpc_endpoint.s3_interface.id}",
      ]
    }
  }
}

これで、追加後に払い出されたエンドポイントかIPアドレスにアクセスすることで、Amazon S3のオブジェクトを取得できる。Amazon S3はHostヘッダで正しいリージョンにアクセスしているかの検証をしているようなので、IPアドレス指定の場合は以下のようにリスクエストヘッダを設定する必要がある(リクエストヘッダがないと301応答で正しくコンテンツの取得ができない)。

curl -H 'Host:[バケット名].s3.[リージョン名].amazonaws.com' http://XXX.XXX.XXX.XXX/index.html
3
5
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
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?