🔧

AWS WAFでメンテナンスページを実装してみた

に公開

はじめに

こんにちは。スペースマーケットでWebエンジニアしてます、dumbled0reです。
去年の4月に新卒で入社したばかりですが、気づけば1年があっという間に過ぎて2年目になりました。時間の流れは早いですね。

今回はサービスのメンテナンスページをAWS WAFを使用して実装したので、紹介したいと思います!

メンテナンス画面

スペースマーケットでメンテナンス中に表示される画面です。

背景

データベースのアップグレード作業に伴い、サービス全体を一時的に停止する必要がありました。
ユーザーにはアクセス制限をかけつつ、開発者は内部確認ができる状態を目指しました。

ALB(Application Load Balancer)のリスナールールにメンテンナンスルールを追加して対応することも検討しましたが、以下の理由からAWS WAFを使用するようにしました。

  1. ALBのルール制限に引っかかる
    • ルールに追加できる条件は最大5つまでと制限がある。
    • メンテナンス中に開発者がサービスにアクセスできるようにしたかったので、複数のIPアドレスを条件に追加すると最大数を超える。
  2. 複数のALBに同様のルールを適用する必要があった
    • アプリケーション単位でALBが存在するため、同じメンテナンスルールを個別に追加する必要がある。
    • WAFをALBに紐づけておけばルールを一元管理できるため、サービス全体をメンテナンス状態にするようなケースでは運用が簡素化できる。

AWS WAFとは

AWS WAF(Web Application Firewall) は、AWS上で動作するアプリケーションに対して、不正なリクエストを遮断するためのセキュリティサービスです。

WAFでは、「Web ACL(Web Access Control List)」という単位でルールを定義し、それをALBやAPI Gatewayなどのリソースにアタッチすることで機能します。Web ACLの中に複数のルールを設定し、リクエストごとに「許可」「ブロック」「カウント」といった動作を指定できます。

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

Terraformでの実装

今回のメンテナンスページをAWS WAFで表示するための設定をTerraformのコード例とあわせて簡単に紹介します。

1. 特定のIPアドレスからサービスにアクセスするためのIP setsの実装

addressesはメンテナンスのたびに変更される可能性があるため、Terraformの管理下から外すようにしています。

aws_wafv2_ip_set.tf
resource "aws_wafv2_ip_set" "example_allow_ip_set" {
  name               = "example_allow_ip_set"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  description        = "IP addresses used for maintenance"
  # メンバーのIPを登録
  addresses = ["xxx.xxx.xxx.xxx/32"]

  lifecycle {
    ignore_changes = [addresses]
  }
}

2. AWS WAFの実装

maintenance_modeというフラグが有効なときだけ、WAFルールとメンテナンス画面のレスポンスが適用されるようにしています。許可されたIPアドレス以外からのアクセスには、HTML形式のメンテナンス画面を返すように設定しています。

aws_wafv2_web_acl.tf
resource "aws_wafv2_web_acl" "example_web_acl" {
  description   = "Example Web ACL for maintenance mode"
  name          = "example_web_acl"
  scope         = "REGIONAL"
  tags          = {}

  default_action {
    allow {}
  }

  dynamic "rule" {
    for_each = var.maintenance_mode ? [1] : []
    content {
      name     = "maintenance_rule"
      priority = 0
      statement {
        not_statement {
          statement {
            ip_set_reference_statement {
              arn = aws_wafv2_ip_set.example_allow_ip_set.arn
            }
          }
        }
      }

      action {
        block {
          custom_response {
            response_code = 503
            custom_response_body_key = "maintenance"
          }
        }
      }

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

  dynamic "custom_response_body" {
    for_each = var.maintenance_mode ? [1] : []
    content {
      key = "maintenance"
      content_type = "TEXT_HTML"
      content = <<EOT
      <!DOCTYPE html>
        <html lang="ja">
          <head>
            <meta charset="UTF-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1" />
            <title>ただいまメンテナンス中です</title>
          </head>
          <body style="text-align:center; padding-top: 50px;">
            <h1>メンテナンス中</h1>
          </body>
        </html>
      EOT
    }
  }

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

3. WAFと特定のリソースとの紐づけ

2で作成したWAFをALBに紐づけます。
example_load_balancerは事前に作成済みのALBリソースを想定しています。

aws_wafv2_web_acl_association.tf
resource "aws_wafv2_web_acl_association" "example_web_acl_association" {
  resource_arn = aws_lb.example_load_balancer.arn
  web_acl_arn  = aws_wafv2_web_acl.example_web_acl.arn
}

これで、Terraformを使ったAWS WAFによるメンテナンスページの実装が出来ました!!

さいごに

今回はAWS WAFを使用したメンテナンスページの実装を紹介しました。
WAFはセキュリティ目的で利用される印象が強かったのですが、メンテナンス対応にも柔軟に活用できることを知れて、学びの多い取り組みとなりました。

スペースマーケットでは一緒にサービスを成長させていく仲間を探しています!
新卒でもフロントエンドからインフラまで幅広く関わることでき、成長できる環境があります。
ご興味ある方はこちらからご応募お待ちしております!

スペースマーケット Engineer Blog

Discussion