メインコンテンツへスキップ

TerraformでAWS WAFのIP制限を設定する方法|ALB・CloudFront対応の実践ガイド

·4559 文字·10 分
目次

AWSで管理画面やステージング環境を公開する際、IP制限を入れ忘れたまま公開してしまうというのは非常によくある事故です。セキュリティグループだけでは防ぎきれないケースも多く、確実なアクセス制御にはAWS WAFの活用が効果的です。

この記事では、Terraformを使ってAWS WAFのIP許可リスト(Allow List)を構築し、ALBやCloudFrontへ適用する方法を、ステップごとの実装例付きで解説します。「自分のIPを許可し忘れてアクセス不可になる」「CloudFrontでscopeを間違えてデプロイ失敗」といった、よくあるハマりポイントもあわせて紹介します。


AWS WAFとは
#

AWS WAF(Web Application Firewall)は、Webアプリケーションへの不正なアクセスを検知・ブロックするためのAWSマネージドサービスです。ALB、CloudFront、API Gatewayなどの前段に配置して、以下のようなルールでトラフィックを制御できます。

ルールの種類用途
IP制限特定のIPアドレスのみ許可/ブロック
SQLインジェクション対策悪意あるSQLクエリを検知しブロック
XSS対策クロスサイトスクリプティング攻撃を防止
レート制限短時間に大量のリクエストを送るIPをブロック
国別アクセス制限特定の国からのアクセスを許可/拒否
Bot対策悪質なBot・クローラーをブロック

本記事ではこの中でも、実務で最も頻繁に利用されるIPアドレス制限にフォーカスします。


IP制限が必要になる場面
#

筆者が実際にIP制限を導入したケースを紹介します。

ユースケース具体例理由
管理画面の保護admin.example.com社内の管理者のみアクセスを許可し、外部からの不正ログイン試行を防ぐ
ステージング環境stg.example.com開発チームとクライアントのみに限定公開し、検索エンジンにインデックスされるのを防ぐ
開発環境dev.example.com社内ネットワークからのみアクセス可能にし、開発中の不安定な状態を外部に露出しない
メンテナンス時example.comリリース直前に動作確認するため、一時的に開発チームのIPのみ許可する

全体の構成
#

今回構築するインフラの構成は以下の通りです。

Internet
AWS WAF ← ここでIPを判定(許可 or ブロック)
Application Load Balancer
ECS / EC2(アプリケーション)

WAFがALBの前段でリクエストを受け取り、許可リストに含まれるIPからのアクセスのみALBへ転送します。許可リストに含まれないIPからのアクセスは、WAFが自動的に 403 Forbidden を返します。


TerraformでIP制限を実装する(3ステップ)
#

Step 1:IPSetを作成する
#

まず、許可するIPアドレスを登録するためのIPSet(IPアドレスの集合)を作成します。

resource "aws_wafv2_ip_set" "allowed_ips" {
  name               = "allowed-office-ips"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"

  addresses = [
    "203.0.113.10/32",   # 本社オフィス
    "198.51.100.20/32",  # リモートワーク拠点
  ]

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}
パラメータ説明
nameIPSetの識別名。AWS管理画面でも表示される
scopeALB/API Gatewayなら REGIONAL、CloudFrontなら CLOUDFRONT
ip_address_versionIPV4 または IPV6
addressesCIDR形式のIPアドレスリスト。/32 は単一IPを意味する

Step 2:Web ACLを作成する
#

次に、WAFのルール本体であるWeb ACLを作成します。ここがIP制限の核心部分です。

resource "aws_wafv2_web_acl" "main" {
  name  = "ip-restriction-acl"
  scope = "REGIONAL"

  # ★ 重要:デフォルトは「ブロック」
  default_action {
    block {}
  }

  # 許可IPからのアクセスのみ通す
  rule {
    name     = "allow-office-ips"
    priority = 1

    action {
      allow {}
    }

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

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AllowOfficeIPs"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "IPRestrictionACL"
    sampled_requests_enabled   = true
  }

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
  }
}

default_action { block {} } がIP制限の肝です。この設定により、「どのルールにもマッチしないリクエスト」はすべてブロックされます。つまり、明示的に許可したIPからのアクセスだけが通過する仕組みです。

Step 3:ALBに関連付ける
#

作成したWeb ACLをALBに適用します。

resource "aws_wafv2_web_acl_association" "alb" {
  resource_arn = aws_lb.main.arn
  web_acl_arn  = aws_wafv2_web_acl.main.arn
}

あとは terraform plan で差分を確認し、terraform apply で適用すればIP制限が有効になります。


変数でIPアドレスを管理する
#

実務ではIPアドレスをハードコードせず、変数として外出しにするのが一般的です。環境ごとにIPリストを切り替えやすくなります。

# variables.tf
variable "allowed_ips" {
  description = "WAFで許可するIPアドレスのリスト(CIDR形式)"
  type        = list(string)
}

variable "environment" {
  description = "環境名(dev / stg / prod)"
  type        = string
}
# terraform.tfvars(環境ごとに用意)
allowed_ips = [
  "203.0.113.10/32",   # 本社
  "198.51.100.20/32",  # リモート拠点
  "192.0.2.50/32",     # VPN出口
]

environment = "production"
# waf.tf(IPSetのaddressesに変数を渡す)
resource "aws_wafv2_ip_set" "allowed_ips" {
  name               = "${var.environment}-allowed-ips"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  addresses          = var.allowed_ips
}

この構成にしておけば、dev.tfvars / stg.tfvars / prod.tfvars を使い分けることで、環境ごとに異なるIPリストを安全に管理できます。


ALBとCloudFrontの違い
#

WAFのIP制限をALBとCloudFrontのどちらに適用するかによって、設定が異なります。最もよくあるエラーが**scope の指定ミス**です。

項目ALB / API GatewayCloudFront
scopeREGIONALCLOUDFRONT
デプロイリージョンALBと同じリージョンus-east-1 固定
provider 指定不要(デフォルトリージョン)provider = aws.virginia 等が必要

CloudFrontにWAFを適用する場合、Terraformのproviderを us-east-1 に設定する必要がある点が特に重要です。

# CloudFront用のprovider(us-east-1)
provider "aws" {
  alias  = "virginia"
  region = "us-east-1"
}

# CloudFront用のIPSet・Web ACLにはこのproviderを指定
resource "aws_wafv2_ip_set" "allowed_ips_cf" {
  provider           = aws.virginia
  name               = "allowed-ips-cloudfront"
  scope              = "CLOUDFRONT"
  ip_address_version = "IPV4"
  addresses          = var.allowed_ips
}
重要

注意: CloudFrontにWAFを適用する場合、scope = "REGIONAL" のまま適用しようとすると「WAFとCloudFrontのscopeが一致しない」というエラーになります。CloudFrontの場合は必ず CLOUDFRONT を指定し、かつリージョンは us-east-1 (バージニア北部) を指定する必要があります。


AWS Managed Rulesとの併用
#

実務ではIP制限だけでなく、AWSが提供するManaged Rules(事前構築済みのセキュリティルール)を併用するのが一般的です。IP制限に加えて、SQLインジェクションやXSSなどの攻撃も自動で防御できます。

resource "aws_wafv2_web_acl" "main" {
  name  = "ip-restriction-acl"
  scope = "REGIONAL"

  default_action {
    block {}
  }

  # ルール1:許可IPリスト
  rule {
    name     = "allow-office-ips"
    priority = 1

    action {
      allow {}
    }

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

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AllowOfficeIPs"
      sampled_requests_enabled   = true
    }
  }

  # ルール2:AWS Managed Rules(許可されたIP内でさらに防御)
  rule {
    name     = "aws-managed-common"
    priority = 2

    override_action {
      none {}
    }

    statement {
      managed_rule_group_statement {
        vendor_name = "AWS"
        name        = "AWSManagedRulesCommonRuleSet"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWSManagedCommon"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "IPRestrictionACL"
    sampled_requests_enabled   = true
  }
}

注意: Managed Rulesでは action ではなく override_action { none {} } を使用します。これはManaged Rules内の各ルールが独自のアクションを持っているため、それをそのまま適用するという意味です。


WAFの料金目安
#

AWS WAFは従量課金制です。小規模な環境であればコストは最小限に抑えられます。

課金項目料金(2026年5月時点、東京リージョン)
Web ACL$5.00 / 月
ルール(1つあたり)$1.00 / 月
リクエスト処理$0.60 / 100万リクエスト

: Web ACL 1つ + IPルール 1つ + Managed Rules 1つの構成で、月間100万リクエストの場合:

$5.00(ACL)+ $1.00(IPルール)+ $1.00(Managed Rules)+ $0.60(リクエスト)= 約$7.60/月

開発・ステージング環境であればリクエスト数も少ないため、月額$7〜10程度で運用できるケースがほとんどです。


よくあるハマりポイントと対策
#

1. 自分のIPを許可し忘れてアクセス不可になる
#

これは最もよくある事故です。terraform apply した瞬間に自分自身がブロックされます。

対策: 適用前に以下のコマンドで現在のグローバルIPを確認し、許可リストに含まれているか必ずチェックしてください。

curl -s https://checkip.amazonaws.com

万が一ロックアウトされた場合は、AWSマネジメントコンソール(WAFの管理画面はIPブロック対象外)からIPSetに自分のIPを追加するか、terraform apply を別のネットワーク(スマホのテザリング等)から実行してください。

2. NAT Gateway経由でIPが変わっている
#

企業のオフィスネットワークでは、PCから直接インターネットに出るのではなく、NAT GatewayやProxyを経由している場合があります。この場合、許可すべきIPはPCのローカルIPではなく、NATの出口IPです。

ネットワーク管理者にグローバルIPを確認するか、上記の checkip.amazonaws.com で実際の出口IPを確認してください。

3. IPv6アクセスへの対応漏れ
#

最近はIPv6でアクセスするユーザーが増えています。IPv4のIPSetだけ作成してIPv6を許可し忘れると、IPv6経由のアクセスがすべてブロックされます。

必要に応じて、IPv6用のIPSetも別途作成してください。

resource "aws_wafv2_ip_set" "allowed_ips_v6" {
  name               = "allowed-office-ips-v6"
  scope              = "REGIONAL"
  ip_address_version = "IPV6"
  addresses          = ["2001:db8::/32"]
}

4. terraform destroyでWAFを削除してしまう
#

terraform destroy でインフラを削除する際、WAFも一緒に消えてしまいます。本番環境で destroy を実行する場合は、WAFリソースに lifecycle { prevent_destroy = true } を追加しておくと安全です。


まとめ
#

ポイント内容
WAFの役割ALB/CloudFrontの前段でリクエストをフィルタリング
IP制限の仕組みdefault_action { block {} } で全ブロック → 許可IPのみALLOW
実装手順IPSet作成 → Web ACL作成 → ALBに関連付け
ALBとCFの違いALBは REGIONAL、CloudFrontは CLOUDFRONT + us-east-1
変数管理terraform.tfvars で環境ごとにIPリストを分離
Managed RulesIP制限と併用してSQLi/XSS/Bot対策も実装
料金小規模なら月額$7〜10程度

AWS環境で管理画面や開発環境を保護する際は、まずWAFによるIP制限から導入するのがおすすめです。Terraformでコード管理すれば、設定ミスの防止・Git管理によるレビュー・環境間の設定統一が実現できます。


よくある質問(FAQ)
#

Q. セキュリティグループのIP制限とWAFの違いは何ですか?
#

セキュリティグループはEC2/ALBレベルのL3/L4(IPアドレス・ポート)のフィルタリングです。WAFはL7(HTTP/HTTPS)レベルで、URLパス・ヘッダー・リクエストボディなどの内容も含めた高度なフィルタリングが可能です。IP制限だけならセキュリティグループでも可能ですが、将来的にレート制限やBot対策を追加したい場合はWAFが適しています。

Q. WAFを適用するとレスポンス速度は遅くなりますか?
#

ほぼ影響ありません。AWSの公式ドキュメントによると、WAFによるレイテンシの増加は通常1ms未満です。IP制限のような単純なルールであれば、パフォーマンスへの影響は無視できるレベルです。

Q. IPが頻繁に変わる場合はどうすればいいですか?
#

リモートワークなどでIPが固定できない場合は、VPN(AWS Client VPN等)を導入し、VPNの出口IPをWAFで許可する方法が一般的です。あるいは、IP制限の代わりにCognito認証を使ったアクセス制御も検討してください。

Q. terraform applyの前にWAFの動作をテストできますか?
#

Web ACLの default_action を一時的に count {} に変更すると、ブロックせずにログだけ記録する「カウントモード」で動作確認できます。CloudWatch LogsやS3にWAFログを出力して、想定通りのIPがマッチするか確認してから block {} に切り替えましょう。

著者
ゆーふー
Web開発、インフラ、AI技術に興味があるエンジニアです。日々の学びを記録しています。

関連記事

👤 運営者プロフィール

運営者プロフィール画像

ゆーふー

メガベンチャーで働く現役Webエンジニア(歴約2年)。
フロントエンドからインフラ構築、セキュリティ対策まで、実務で得た「現場のリアルな技術知見」を発信しています。