🕌

AWS学びなおし(+TF)_ネットワークACL

2025/02/24に公開

ネットワークACL(ネットワークアクセスコントロールリスト)は、AWS VPC(Virtual Private Cloud)のサブネットレベルで動作する、ステートレスなファイアウォールです。サブネットへの出入り口でトラフィックを制御し、セキュリティを強化するために使用されます。

主な特徴とポイント:

  • サブネットレベル: 特定のサブネットに出入りするトラフィック全体を制御します。個々のEC2インスタンスではなく、サブネット全体に適用される点がセキュリティグループとの大きな違いです。
  • ステートレス: ネットワークACLは、過去の接続状態を記憶しません。インバウンドとアウトバウンドのルールは独立して評価されます。つまり、インバウンドで許可された通信であっても、アウトバウンドルールで明示的に許可しなければ応答トラフィックはブロックされます。
  • ルールリスト: 番号付きのルールのリストで構成されます。ルールは番号の小さい順に評価され、最初に一致したルールが適用されます。
  • 許可ルールと拒否ルール: トラフィックを許可する「許可」ルールと、明示的に拒否する「拒否」ルールを設定できます。
  • デフォルトACLとカスタムACL: VPC作成時に自動的に作成される「デフォルトネットワークACL」と、ユーザーが独自に作成する「カスタムネットワークACL」があります。
    • デフォルトネットワークACL: 全てのインバウンドとアウトバウンドトラフィックを許可する設定になっています。
    • カスタムネットワークACL: デフォルトでは全てのインバウンドとアウトバウンドトラフィックを拒否する設定になっています。必要に応じて許可ルールを設定する必要があります。
  • 暗黙的な拒否: どのルールにも一致しないトラフィックは、暗黙的に拒否されます。
  • 適用対象: サブネットに関連付けられます。1つのサブネットに1つのネットワークACLのみ関連付けることができます。ただし、複数のサブネットに同じネットワークACLを関連付けることは可能です。
  • 評価対象: ネットワークACLは、サブネットに出入りする全てのトラフィックを評価します。これには、EC2インスタンス間の通信、インターネットとの通信、VPCピアリング経由の通信などが含まれます。

ネットワークACLの構成要素:

  • ルール番号: ルールの評価順序を決定する番号です。小さい番号ほど優先度が高くなります。推奨されるルール番号の刻み幅は10または100です。これにより、後からルールを追加・挿入する際に番号の競合を避けやすくなります。
  • タイプ: ルールの種類を指定します。
    • カスタム TCP ルール: TCPプロトコルに基づいたルール
    • カスタム UDP ルール: UDPプロトコルに基づいたルール
    • カスタム ICMP ルール: ICMPプロトコルに基づいたルール
    • すべての TCP: 全てのTCPトラフィック
    • すべての UDP: 全てのUDPトラフィック
    • すべての ICMP: 全てのICMPトラフィック
    • すべての IPv4/IPv6 ICMP: IPv4/IPv6両方のICMPトラフィック
    • ソース/デスティネーション: 送信元または宛先を指定します。
    • エコー応答: ICMPエコー応答 (ping応答)
    • エコー要求: ICMPエコー要求 (ping要求)
    • Time Exceeded: ICMP Time Exceeded
    • など、様々なプロトコルや目的に応じたタイプが用意されています。
  • プロトコル: TCP、UDP、ICMP、またはすべてのプロトコル (-1) を指定します。
  • ポート範囲: ポート番号またはポート範囲を指定します。すべてのポートを許可する場合は 0-65535 を指定します。
  • 送信元/宛先: トラフィックの送信元または宛先CIDRブロックを指定します。
  • 許可/拒否 (許可または拒否): ルールに一致するトラフィックを許可するか拒否するかを指定します。

ネットワークACLとセキュリティグループの違い:

特徴 ネットワークACL セキュリティグループ
動作レベル サブネットレベル インスタンスレベル
ステートフル/ステートレス ステートレス ステートフル
デフォルト動作 デフォルトACL: 全許可、カスタムACL: 全拒否 デフォルト: 全拒否
ルール評価 番号付きルールリスト、番号順に評価、最初の一致ルール適用 全ての許可ルールを評価、拒否ルールは存在しない
拒否ルール 明示的な拒否ルールを設定可能 拒否ルールは存在しない (許可ルールのみ)
適用対象 サブネット EC2インスタンス、ENI (Elastic Network Interface)
ルールの複雑性 比較的シンプル より複雑な設定が可能 (ステートフル、送信元/宛先セキュリティグループなど)

使い分けのポイント:

  • ネットワークACL: サブネット全体のトラフィックを大まかに制御したい場合、ステートレスなフィルタリングで高速な処理が必要な場合、明示的にトラフィックを拒否したい場合に有効です。例えば、DMZサブネットへの不要なポートへのアクセスをブロックしたり、特定のIPアドレスからのアクセスを拒否したりする場合に使用します。
  • セキュリティグループ: 個々のEC2インスタンスに対して、よりきめ細かく、ステートフルな制御を行いたい場合に適しています。アプリケーションごとに必要なポートのみを許可したり、同じセキュリティグループに所属するインスタンス間の通信を許可したりする場合に使用します。

通常は、セキュリティグループをメインのセキュリティ対策とし、ネットワークACLをサブネットレベルでの追加の防御層として使用することが推奨されます。両者を組み合わせることで、多層防御を実現し、より堅牢なセキュリティ体制を構築できます。

【実務レベルの内容と重要事項】

  • 最小権限の原則: ネットワークACLのルールは、必要最小限の許可ルールのみを設定し、デフォルト拒否の原則に従うことが重要です。不要なポートやプロトコルは明示的に拒否するルールを設定することで、セキュリティリスクを低減できます。
  • ルール番号の計画: ルール番号は、後からルールを追加・変更する際に重要になります。10または100刻みでルール番号を設定し、ルールの追加や挿入に備えて番号に余裕を持たせておくことが推奨されます。
  • ルールの整理と可読性: ネットワークACLのルールが増えてくると、管理が煩雑になりがちです。ルールを目的別にグループ化したり、コメントを活用したりするなど、ルールの整理と可読性を意識することが重要です。TerraformなどのIaCツールで管理することで、コードとしての可読性・管理性を向上させることができます。
  • ログ記録と監視: ネットワークACL自体にはログ記録機能はありません。しかし、VPCフローログと組み合わせることで、ネットワークACLを通過するトラフィックのログを記録し、監視することができます。これにより、セキュリティイベントの分析やトラブルシューティングに役立てることができます。
  • セキュリティグループとの連携: ネットワークACLとセキュリティグループは、それぞれ異なるレイヤーでセキュリティを強化する役割を担います。両者を適切に組み合わせることで、より効果的な多層防御を実現できます。例えば、ネットワークACLでサブネットレベルでの大まかなアクセス制御を行い、セキュリティグループでインスタンスレベルでの詳細なアクセス制御を行うといった使い分けが考えられます。
  • 一時的なルールの適用: 一時的に特定のIPアドレスからのアクセスを許可したい場合など、ネットワークACLのルールを一時的に変更することがあります。Terraformで管理している場合は、terraform apply で迅速にルールを適用できます。
  • 変更管理: ネットワークACLのルール変更は、ネットワーク全体の通信に影響を与える可能性があります。変更を行う際は、事前に影響範囲を十分に検討し、変更内容を記録しておくことが重要です。TerraformなどのIaCツールで変更履歴を管理することで、変更管理を効率化できます。
  • デフォルトネットワークACLの扱い: デフォルトネットワークACLは全て許可の設定になっているため、そのまま運用するとセキュリティリスクが高い状態となります。特別な理由がない限り、カスタムネットワークACLを作成し、デフォルトネットワークACLは使用しないようにすることが推奨されます。もしデフォルトネットワークACLを使用する場合は、必要に応じてルールを修正し、セキュリティを強化する必要があります。
  • アウトバウンドルールの重要性: ステートレスなネットワークACLでは、インバウンドだけでなくアウトバウンドルールも重要です。インバウンドで許可した通信に対する応答トラフィックをアウトバウンドルールで許可しないと、通信が正常に確立しません。特に、エフェメラルポートを使用する通信(例:HTTP)では、アウトバウンドのポート範囲を適切に設定する必要があります。

実務での注意点:

  • ルール数の制限: ネットワークACLには、ルール数の上限があります (AWSのドキュメントで最新の制限値を確認してください)。ルール数が上限に近づいてきた場合は、ルールの整理や統合を検討する必要があります。
  • パフォーマンスへの影響: ネットワークACLは、サブネットに出入りする全てのトラフィックを評価するため、大量のトラフィックが発生する環境では、パフォーマンスに影響を与える可能性があります。ただし、通常はセキュリティグループの方がパフォーマンスへの影響は大きいため、ネットワークACLによるパフォーマンス劣化は限定的です。
  • VPCエンドポイントとの関係: VPCエンドポイントを使用する場合、ネットワークACLの設定に注意が必要です。VPCエンドポイントは、プライベートサブネットからAWSサービスへのアクセスを可能にするものですが、ネットワークACLでVPCエンドポイントへのアクセスをブロックしてしまうと、VPCエンドポイントが正常に機能しません。VPCエンドポイントを使用する場合は、ネットワークACLでVPCエンドポイント関連のトラフィックを許可するルールを設定する必要があります。

【実務でどの程度使用されるのか】

ネットワークACLは、セキュリティグループほど頻繁には使用されないものの、実務環境においても重要な役割を果たします。

利用頻度が高いケース:

  • DMZ (Demilitarized Zone) の構築: インターネットに公開するWebサーバーなどを配置するDMZサブネットでは、ネットワークACLを使用して、インターネットからの不要なアクセスを厳格に制限し、セキュリティを強化することが一般的です。例えば、WebサーバーへのHTTP/HTTPS (80/443) ポートのみを許可し、他のポートへのアクセスは拒否するルールを設定します。
  • 特定のIPアドレスからのアクセス制限: 特定のIPアドレス範囲からのアクセスのみを許可したい場合や、特定のIPアドレスからのアクセスを拒否したい場合に、ネットワークACLが有効です。例えば、社内ネットワークからのアクセスのみを許可するルールや、悪意のあるIPアドレスからのアクセスを拒否するルールを設定します。
  • サブネットレベルでの一律的なセキュリティポリシー適用: 複数のEC2インスタンスが配置されたサブネット全体に対して、一律的なセキュリティポリシーを適用したい場合に、ネットワークACLが役立ちます。例えば、特定のプロトコルやポートへのアクセスをサブネット全体で禁止するルールを設定します。
  • シンプルなルールでの高速なフィルタリング: ステートレスなネットワークACLは、セキュリティグループに比べて処理が高速です。大量のトラフィックを高速にフィルタリングしたい場合に、ネットワークACLが適しています。
  • セキュリティ監査要件への対応: セキュリティ監査要件で、ネットワークレベルでのアクセス制御が求められる場合があります。ネットワークACLは、そのような監査要件に対応するための有効な手段となります。

利用頻度が低いケース:

  • シンプルなWebアプリケーション: シンプルなWebアプリケーションなど、セキュリティ要件が比較的緩やかな環境では、セキュリティグループのみで十分なセキュリティを確保できる場合があります。
  • 開発環境やテスト環境: 開発環境やテスト環境など、セキュリティよりも利便性を重視する環境では、ネットワークACLを省略する場合があります。ただし、本番環境と同等のセキュリティレベルを求める場合は、開発環境やテスト環境でもネットワークACLを導入することが望ましいです。
  • セキュリティグループで十分な場合: セキュリティグループの機能で十分なセキュリティを確保できる場合、あえてネットワークACLを追加する必要はありません。例えば、インスタンスごとのきめ細かなアクセス制御が主な要件である場合などです。

実務での判断基準:

ネットワークACLを導入するかどうかは、以下の要素を考慮して判断します。

  • セキュリティ要件: システムのセキュリティ要件の高さ。機密性の高い情報を扱うシステムや、外部からの攻撃のリスクが高いシステムでは、ネットワークACLによる多層防御が有効です。
  • コンプライアンス要件: 業界の規制やセキュリティ監査要件で、ネットワークレベルでのアクセス制御が求められるかどうか。
  • 運用コスト: ネットワークACLの設定・管理にかかる運用コスト。ルール数が多くなると管理が煩雑になるため、運用コストも考慮する必要があります。
  • 複雑性: ネットワークACLを導入することで、ネットワーク構成が複雑になるかどうか。シンプルな構成を維持したい場合は、ネットワークACLを省略することも検討できます。
  • パフォーマンス要件: 大量のトラフィックが発生する環境では、ネットワークACLのパフォーマンスへの影響を考慮する必要があります。ただし、通常はセキュリティグループの方がパフォーマンスへの影響は大きいため、ネットワークACLによるパフォーマンス劣化は限定的です。

まとめ:

ネットワークACLは、必須ではありませんが、セキュリティを強化するための重要な選択肢の一つです。特に、DMZの構築や、特定のIPアドレスからのアクセス制限など、サブネットレベルでのアクセス制御が必要な場合には、積極的に活用を検討すべきです。セキュリティ要件、運用コスト、複雑性などを総合的に考慮し、適切な判断を行うことが重要です。

【terraformのコードで記述する場合の基本的内容と実務レベルの内容】

基本的な記述例:

resource "aws_network_acl" "example" {
  vpc_id = aws_vpc.example.id
  subnet_ids = [aws_subnet.example.id] # 適用するサブネットID

  tags = {
    Name = "example-network-acl"
  }
}

resource "aws_network_acl_rule" "ingress_http" {
  network_acl_id = aws_network_acl.example.id
  rule_number    = 100 # ルール番号
  rule_action    = "allow" # 許可
  protocol       = "tcp" # TCPプロトコル
  cidr_block     = "0.0.0.0/0" # 全てのIPアドレスから
  from_port      = 80 # 80番ポート (HTTP)
  to_port        = 80 # 80番ポート (HTTP)
  egress         = false # イングレスルール (false)
}

resource "aws_network_acl_rule" "egress_http_response" {
  network_acl_id = aws_network_acl.example.id
  rule_number    = 100 # ルール番号
  rule_action    = "allow" # 許可
  protocol       = "tcp" # TCPプロトコル
  cidr_block     = "0.0.0.0/0" # 全てのIPアドレスへ
  from_port      = 1024 # エフェメラルポート範囲 (例)
  to_port        = 65535 # エフェメラルポート範囲 (例)
  egress         = true  # イーグレスルール (true)
}

解説:

  • aws_network_acl リソース: ネットワークACL自体を定義します。
    • vpc_id: ネットワークACLを作成するVPCのIDを指定します。
    • subnet_ids: ネットワークACLを関連付けるサブネットIDのリストを指定します。
    • tags: ネットワークACLにタグを付与します (Nameタグは必須ではありませんが、管理のために推奨されます)。
  • aws_network_acl_rule リソース: ネットワークACLのルールを定義します。
    • network_acl_id: ルールを適用するネットワークACLのIDを指定します。
    • rule_number: ルール番号を指定します。
    • rule_action: ルールのアクション ("allow" または "deny") を指定します。
    • protocol: プロトコル (tcp, udp, icmp, -1 (all)) を指定します。
    • cidr_block: 送信元または宛先CIDRブロックを指定します。
    • from_port, to_port: ポート範囲を指定します。
    • egress: イングレスルール (false) かイーグレスルール (true) かを指定します。

実務レベルの内容:

  • 変数とモジュール化: CIDRブロック、ポート番号、ルール番号などを変数化し、モジュールとして再利用できるようにすることで、コードの可読性・再利用性・メンテナンス性を向上させます。
variable "vpc_id" {}
variable "subnet_id" {}
variable "http_port" {
  default = 80
}
variable "ephemeral_port_range" {
  default = "1024-65535"
}
variable "rule_number_base" {
  default = 100
}

resource "aws_network_acl" "example" {
  vpc_id     = var.vpc_id
  subnet_ids = [var.subnet_id]

  tags = {
    Name = "example-network-acl"
  }
}

resource "aws_network_acl_rule" "ingress_http" {
  network_acl_id = aws_network_acl.example.id
  rule_number    = var.rule_number_base + 10
  rule_action    = "allow"
  protocol       = "tcp"
  cidr_block     = "0.0.0.0/0"
  from_port      = var.http_port
  to_port        = var.http_port
  egress         = false
}

resource "aws_network_acl_rule" "egress_http_response" {
  network_acl_id = aws_network_acl.example.id
  rule_number    = var.rule_number_base + 20
  rule_action    = "allow"
  protocol       = "tcp"
  cidr_block     = "0.0.0.0/0"
  from_port      = split("-", var.ephemeral_port_range)[0]
  to_port        = split("-", var.ephemeral_port_range)[1]
  egress         = true
}
  • 複数のルール定義: 複数のルールを定義する場合は、aws_network_acl_rule リソースを複数記述します。ルール番号を適切に設定し、評価順序を考慮する必要があります。for_eachcount を使用して、ルール定義を効率化することも可能です。
resource "aws_network_acl_rule" "rules" {
  for_each       = {
    ingress_http = {
      rule_number = 100
      action      = "allow"
      protocol    = "tcp"
      from_port   = 80
      to_port     = 80
      egress      = false
    }
    ingress_ssh = {
      rule_number = 110
      action      = "allow"
      protocol    = "tcp"
      from_port   = 22
      to_port     = 22
      cidr_block  = "192.168.0.0/16" # 社内ネットワークからのSSHアクセスのみ許可
      egress      = false
    }
    egress_all = {
      rule_number = 100
      action      = "allow"
      protocol    = "-1" # all protocols
      cidr_block  = "0.0.0.0/0"
      egress      = true
    }
  }

  network_acl_id = aws_network_acl.example.id
  rule_number    = each.value.rule_number
  rule_action    = each.value.action
  protocol       = each.value.protocol
  cidr_block     = lookup(each.value, "cidr_block", "0.0.0.0/0") # cidr_block が定義されていなければ 0.0.0.0/0 をデフォルト値とする
  from_port      = lookup(each.value, "from_port", null) # ポート範囲が定義されていなければ null (すべてのポート)
  to_port        = lookup(each.value, "to_port", null)   # ポート範囲が定義されていなければ null (すべてのポート)
  egress         = each.value.egress
}
  • 既存VPCへの適用: 既存のVPCにネットワークACLを適用する場合は、aws_vpc.example.id を既存のVPCのIDに置き換えます。データソース (data "aws_vpc" "example") を使用して、既存のVPC情報を取得することもできます。
  • import: 既にAWS上に存在するネットワークACLをTerraformで管理する場合は、terraform import コマンドを使用して、Terraform stateに取り込むことができます。
terraform import aws_network_acl.example acl-xxxxxxxxxxxxxxxxx
  • ルール番号の自動管理: rule_number を手動で管理する代わりに、モジュール内で自動的に採番するロジックを組み込むことで、ルール番号の重複や管理ミスを防ぐことができます。
  • 説明 (description) の追加: 各ルールに description を追加することで、ルールの目的や内容を明確にし、可読性を向上させることができます。(Terraformの aws_network_acl_rule リソースには description 属性はありません。コメントを活用するなど、別の方法で説明を記述する必要があります。)
  • 定期的なレビュー: ネットワークACLのルールは、定期的にレビューし、不要なルールを削除したり、ルール内容を修正したりするなど、メンテナンスを行うことが重要です。Terraformで管理することで、ルールの変更履歴を追跡しやすくなり、レビュー作業を効率化できます。

Terraformコードのベストプラクティス:

  • 可読性: 変数名、リソース名、タグなどを分かりやすく命名し、コードの可読性を高めます。
  • 再利用性: モジュール化や変数化を活用し、コードの再利用性を高めます。
  • 冪等性: Terraformコードは冪等性を持つように記述し、何度 terraform apply を実行しても同じ結果になるようにします。
  • バージョン管理: Terraformコードはバージョン管理システム (Gitなど) で管理し、変更履歴を追跡できるようにします。
  • テスト: Terraformコードをデプロイする前に、 terraform plan コマンドで変更内容を確認し、必要に応じてテスト環境で動作確認を行うことが推奨されます。

これらの実務レベルの内容を考慮することで、より堅牢で、管理しやすいネットワークACLをTerraformで構築することができます。

Discussion