🔥

CloudfrontのIP制限をAWS/WAFで行う

2023/06/22に公開

はじめに

CloudFrontではALB等のようにセキュリティグループでIP制限を行うことが出来ません。
CloudFrontでの検証をセキュアに行うため、IP制限をWAFで実現します。

https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/what-is-aws-waf.html

プロビジョニングはTerraformでおこないます。

環境

Terraform 1.4.6

方針

IP Setへ許可したいIPを追加し、
それ以外のリクエストを拒否するようなWeb Aclを作成します。

前提

  • CloudFrontがデプロイ済み
  • 検証用マシン→CloudFrontのリクエストが成功することを確認しておきます。
    検証用マシンのIPを拒否するように設定します。
$ curl -I https://<cloudfrontドメイン>
HTTP/2 200

検証

IP Set

provider "aws" {
  alias  = "virginia"
  region = "us-east-1"
}

variable "allow-ip" {
  type = string
}

resource "aws_wafv2_ip_set" "ipset" {
  provider           = aws.virginia
  name               = "allow-ip"
  description        = "allow ip set"
  scope              = "CLOUDFRONT"
  ip_address_version = "IPV4"
  addresses          = ["${var.allow-ip}/32"]
}

scopeCLOUDFRONTである場合、バージニアリージョン(us-east-1)に作成する必要があります。
そのためのaliasです。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_ip_set

Web Acl

resource "aws_wafv2_web_acl" "web_acl" {
  provider    = aws.virginia
  name        = "only-myip"
  description = "A sample Web ACL that blocks all traffic except for a allow IP set"
  scope       = "CLOUDFRONT"
  default_action {
    block {}
  }

  rule {
    name     = "allow_ips_in_ip_set"
    priority = 1
    action {
      allow {}
    }

    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.ipset.arn
      }
    }

    visibility_config {
      sampled_requests_enabled   = false
      cloudwatch_metrics_enabled = false
      metric_name                = "sample"
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = false
    metric_name                = "sample_web_acl_metric"
    sampled_requests_enabled   = false
  }
}

こちらもバージニアリージョンである必要があります。
ログ,サンプリングの設定が必要である場合、visibility_configを設定します。

CloudFrontディストリビューション

resource "aws_cloudfront_distribution" "cfront" {
// 略
  web_acl_id = aws_wafv2_web_acl.web_acl.arn

CloudFrontディストリビューション側でさきほど作成したWeb Aclを指定します。
また、aws_wafv2_web_acl_associationはCloudFrontでは使用出来ません。
気をつけましょう。

Do not use this resource to associate a WAFv2 Web ACL with a Cloudfront Distribution. The AWS API call backing this resource notes that you should use the web_acl_id property on the cloudfront_distribution instead.

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl_association

動作確認

先程の検証用マシンからリクエストを送ると

$ curl https://<cloudfrontドメイン>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>

このようにWAFに弾かれます。
ステータスコードは403ですね。

ブラウザでの見た目はこんな感じです。

ルールを外す場合

例えば検証完了してフルアクセスにする場合、

resource "aws_cloudfront_distribution" "cfront" {
// 略
-  web_acl_id = aws_wafv2_web_acl.web_acl.arn

このようにweb_acl_idを外してしまえばよいです

参考

こちらの記事を参考にさせていただきました。
https://dev.classmethod.jp/articles/how-to-use-aws-waf-v2-to-filter-incoming-traffic-based-ip-address/
https://oji-cloud.net/2022/09/27/post-7184/

Discussion