[アップデート] AWS Network Firewall のステートフルルールグループが TCP トラフィックの reject action をサポートしました

ポリシーに応じてdropとrejectを使い分けよう
2023.03.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

通信できない場合タイムアウトまで待ちたくないな

こんにちは、のんピ(@non____97)です。

皆さんはAWS Network Firewallで通信をさせない場合にタイムアウトまで待たせたくないと思ったことはありますか? 私はあります。

従来からあるdrop actionだと通信を通さず、送信元に対してレスポンスを何も返しません。そのため、送信元からすると通信が何らかの原因で到達していないのか、それともどこかでブロックされているのか判断することが難しいです。

また、送信元は送信先からのレスポンスがあるまでポートをオープンして待つことになるので、時間的にもリソース的にも勿体無いです。

少し前ですが、AWS Network Firewall のステートフルルールグループが TCP トラフィックの reject action をサポートしました。

これにより、送信元に通信が拒否されたことを伝えることができます。

dropが良いのかrejectが良いのかは考え方によるとは思いますが、選択肢が増えたのは良いことです。

実際に試してみたので紹介します。

いきなりまとめ

  • 5-tupleとSuricata互換のIPSルールのルールグループにおいて、TCP トラフィックのreject actionが追加された
    • ただし、2023/3/10時点ではFTPとIMAPはサポートしていない
  • ドメインリストのルールグループには設定できない
    • ドメインに対してreject actionを設定したい場合は、Suricata互換のIPSルールで行う
  • reject actionのルールにマッチすると、RSTフラグが付与されたパケットが返ってくる
  • Network Firewallが出力したログからはdropなのか、rejectなのか判別することはできない
    • signaturesignature_idから判断する必要がある

ドキュメントの確認

まずはreject actionについてAWS公式ドキュメントから確認してみます。

Reject – Drop traffic that matches the conditions of the stateful rule and send a TCP reset packet back to sender of the packet. A TCP reset packet is a packet with no payload and a RST bit contained in the TCP header flags. Reject is available only for TCP traffic. This option doesn't support FTP and IMAP protocols.

(以下機械翻訳)

Reject - ステートフルルールの条件に一致するトラフィックをドロップし、パケットの送信者にTCPリセットパケットを返送します。TCPリセットパケットとは、ペイロードがなく、TCPヘッダーフラグにRSTビットが含まれるパケットです。Reject は、TCP トラフィックに対してのみ有効です。このオプションは、FTPとIMAPプロトコルをサポートしません。

Defining rule actions in AWS Network Firewall - AWS Network Firewall

reject actionを設定したルールに一致すると、RSTフラグが付与されたパケットが返ってくるようですね。

Suricataのドキュメントも確認します。

  • reject - send RST/ICMP unreach error to the sender of the matching packet.
  • rejectsrc - same as just reject
  • rejectdst - send RST/ICMP error packet to receiver of the matching packet.
  • rejectboth - send RST/ICMP error packets to both sides of the conversation.

6.1. Rules Format — Suricata 6.0.2 documentation

rejectを指定すると、RSTだけではなくICMPメッセージも返すように見えますね。

検証環境

実際にreject actionを試してみます。

検証環境は以下の通りです。

AWS Network Firewall が TCP トラフィックのreject actionをサポートしました検証環境構成図

以下ルールグループについて、reject actionを設定できるか確認します。

  • 5-tuple
  • ドメインリスト
  • Suricata互換のIPSルール

RSTフラグが付与されたTCPパケットが返ってきたかはtcpdumpによるパケットキャプチャーで確認します。その際、以下記事を参考にWiresharkにパケットキャプチャーの結果を転送します。

tcpdumpの結果を転送する際は、EC2インスタンスのTCP/22に対してSSMセッションマネージャーでポートフォワーディングをして、SSHのリモートコマンドでtcpdumpを実行します。

検証環境はAWS CDKでデプロイします。使用したコードは以下リポジトリに保存しています。

npx cdk deployでリソースをデプロイすると、以下のようなOutputsが出力されるので控えておきます。

Outputs:
NetworkFirewallStack.Ec2InstanceAGetSecretKeyCommand9D38ABDA = aws ssm get-parameter --name /ec2/keypair/key-07b974428572e521f --region us-east-1 --with-decryption --query Parameter.Value --output text > ./key-pair/test-key-pair.pem
NetworkFirewallStack.Ec2InstanceAInstanceId6D8E6BA7 = i-08c44ef77a0db8bfe
NetworkFirewallStack.Ec2InstanceAInstancePrivateIpAddress5C73F3CE = 10.1.1.37

drop actionの確認

Wiresharkの設定

reject actionの挙動を実際に確認する前に、drop actionの挙動を確認しておきます。

下準備としてWiresharkの設定をします。

まず、AWS CDKデプロイした時のOutputsに出力されたコマンドを実行して、EC2インスタンスに設定したキーペアの秘密鍵をダウンロードします。

# EC2インスタンスに設定したキーペアの秘密鍵のダウンロード
$ aws ssm get-parameter --name /ec2/keypair/key-07b974428572e521f --region us-east-1 --with-decryption --query Parameter.Value --output text > ./key-pair/test-key-pair.pem

# 権限を 400 に設定
$ chmod 400 ./key-pair/test-key-pair.pem

次にWiresharkのリモートキャプチャー用の設定ファイルを編集します。

設定箇所は以下の2箇所です。

  1. target_private_ipaddr
    • tcpdumpを実行するEC2インスタンスのIPアドレス
    • SSHの通信がパケットキャプチャーのノイズとなるため指定
  2. portforward_instance_id
    • SSMセッションマネージャーでポートフォワーディングするEC2インスタンスのID

どちらもAWS CDKデプロイした時のOutputsに出力されているので、そちらを設定します。

filterportforward_portなどはお好みで変更してください。

./wireshark/wireshark-remote.conf

###############################################################################
# Target Host Setting
#

# EC2インスタンスのキーペア
declare -r key_pair=./key-pair/test-key-pair.pem

# EC2インスタンスにSSHする際のユーザ名
declare -r ssh_user_name=ec2-user

# 接続先EC2インスタンスのホスト名
# PublicのIPアドレスでアクセスできる場合は、EC2のPublic DNSを指定する
# ポートフォワーディングする場合はlocalhostを指定する
declare -r connect_hostname=localhost

###############################################################################
# Capture Setting
#

# チャプチャーインタフェース
declare -r interface=eth0

# キャプチャーIPアドレス(EC2インスタンスのPrivate IPアドレス)
# e.g.) declare -r target_private_ipaddr=10.1.1.4
declare -r target_private_ipaddr=10.1.1.37

# キャプチャーフィルタ
# (通信が大量にある場合は設定することが必須)
# e.g.) FILTER="tcp port 80"
local filter="tcp port 80 or icmp"

###############################################################################
# Portforward Setting (Optional)
#

# ポートフォワーディングする場合はローカルの空きポート番号を指定する(1024番以上)
# ポートフォワーディングを利用しない場合はコメントアウトする
declare -r portforward_port=10022

# 接続先EC2インスタンスのID
# SSMセッションマネージャーでポートフォワーディングする際に使用
# ポートフォワーディングを利用しない場合はコメントアウトする
# e.g.) declare -r portforward_instance_id=i-xxxxxxxxxxxxxxxxx
declare -r portforward_instance_id=i-08c44ef77a0db8bfe

設定が完了したら、./wireshark/wireshark-remote.shを実行します。これにより、SSMセッションマネージャーでTCP/22をローカルマシンのTCP/10022にポートフォワーディングして、TCP/10022に対してSSH経由でtcpdumpを実行してくれます。

$ ./wireshark/wireshark-remote.sh

Starting session with SessionId: botocore-session-1678431778-0c14fa7f5f8aade49
Port 10022 opened for sessionId botocore-session-1678431778-0c14fa7f5f8aade49.
Waiting for connections...

Connection accepted for session [botocore-session-1678431778-0c14fa7f5f8aade49]
 ** (wireshark:97345) 16:03:16.238702 [GUI WARNING] -- Populating font family aliases took 176 ms. Replace uses of missing font family ".AppleSystemUIFont" with one that exists to avoid this cost.
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes

また、自動でWiresharkが起動します。-というインターフェースを選択しておきます。

The_Wireshark_Network_Analyzer

これで下準備はOKです。

ちなみに、./wireshark/wireshark-remote.shは以下の通りです。パケットキャプチャーを終了したい場合はCtrl + Cです。SSMセッションマネージャーのポートフォワーディングやWiresharkのプロセスも一緒に落ちるようにしています。

./wireshark/wireshark-remote.sh

#!/bin/bash
# Name
#   wireshark-remote
#
# format
#   wireshark-remote.sh [-c config_path]
#
# description
#   ssh経由でtcpdumpを実行した結果をwiresharkに転送することによって
#   間接的にwiresharkによるリモートキャプチャーする
#
# requirements
#   1. !requiretty設定
#     リモートホスト(EC2)でtcpdumpを実行するユーザ(ec2-user)に
#     コマンド(tcpdump)を実行するときにTTYを要求しない設定を追加する
#     例.ec2-userの場合、visudoにて以下の行を追加
#       Defaults:ec2-user !requiretty
#
#   2. Wiresharのキャプチャー開始
#     このシェルスクリプトを実行して起動したWiresharkのインターフェイスで - を選択する
#
# return
#      0 =  SUCCESS
#      0 != FAILED
#

set -euo pipefail

# kill child process
trap 'pkill -P $$' HUP
trap 'pkill -P $$' INT
trap 'pkill -P $$' TERM

##############################################################################
# Main
#

function main () {
  declare -r snaplen=262144
  declare -r keep_alive=60

  local portforward_option
  local config
  local result

  # include config. (default: wireshark-remote.conf)
  if [[ "$#" == 2 && "$1" == "-c" ]]; then
    config="$2"
  else
    config="./wireshark/wireshark-remote.conf"
  fi

  # load config
  if [[ ! -f "${config}" ]]; then
    echo "Invalid config file : ${config}"
    exit 1
  fi
  source "${config}"

  # Portforwarding as child process.
  if [[ -n "${portforward_port}" ]]; then

    aws ssm start-session --target "${portforward_instance_id}" \
      --document-name AWS-StartPortForwardingSession \
      --parameters '{"portNumber":["22"],"localPortNumber":["'"${portforward_port}"'"]}' &

    portforward_option="-p ${portforward_port}"
  fi

  sleep 5

  # set Filter
  if [[ -n "${filter}" ]]; then
    filter="and \(${filter}\)"
  fi

  # remote command
  ssh \
    -i "${key_pair}" \
    -C \
    -o ServerAliveInterval="${keep_alive}" \
    -o StrictHostKeyChecking=no \
    "${ssh_user_name}@${connect_hostname}" \
    "${portforward_option}" \
    sudo tcpdump \
      -U \
      -i "${interface}" \
      -s "${snaplen}" \
      -w - \
      "not \(host ${target_private_ipaddr} and tcp port 22\) ${filter}" \
    | wireshark -i -
  
  result=$?
  if (( ${result} == 0 )); then
    pkill -P $$
  fi

  exit ${result}
}

main "$@"

パケットキャプチャーの確認

下準備ができたので、実際にパケットキャプチャーをしてdrop actionの挙動を確認してみます。

定義したルールグループのルールは以下の通りです。HTTP/80に対してdropするようにしています。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "DROP",
            "Header": {
                "Protocol": "HTTP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "msg",
                    "Settings": [
                        "\"HTTP drop\""
                    ]
                },
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000001"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        }
    ]
}

この状態で、EC2インスタンスからHTTP/80の通信をしようとしてみます。

$ curl -m 5 -I http://dev.classmethod.jp
curl: (28) Operation timed out after 5000 milliseconds with 0 bytes received

タイムアウトとなりましたね。

Wiresharkでこの時の通信を確認してみます。

HTTPのDROP設定時のパケットキャプチャ

TCPのコネクション接続時にSYN + ACKが帰ってきていますが、HEAD / HTTP/1.1のリクエストに対するレスポンスが返ってきていないですね。最終的には諦めのFIN + ACKがクライアントから投げられています。

CloudWatch Logsに出力されたログを確認すると、以下のようなログが出力されていました。

{
    "firewall_name": "network-firewall",
    "availability_zone": "us-east-1a",
    "event_timestamp": "1678432026",
    "event": {
        "app_proto": "http",
        "src_ip": "10.1.1.37",
        "src_port": 37954,
        "event_type": "alert",
        "alert": {
            "severity": 3,
            "signature_id": 1000001,
            "rev": 1,
            "signature": "HTTP drop",
            "action": "blocked",
            "category": ""
        },
        "flow_id": 18983069345273,
        "dest_ip": "76.223.57.58",
        "proto": "TCP",
        "http": {
            "hostname": "dev.classmethod.jp",
            "url": "/",
            "http_user_agent": "curl/7.88.1",
            "http_method": "HEAD",
            "protocol": "HTTP/1.1",
            "length": 0
        },
        "dest_port": 80,
        "timestamp": "2023-03-10T07:07:06.324930+0000"
    }
}

reject actionの確認

5-tuple ルールグループの場合

それでは、reject actionの挙動を確認していきます。

まずは、5-tuple ルールグループの場合です。

定義したルールグループのルールは以下の通りです。HTTP/80に対してrejectするようにしています。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "REJECT",
            "Header": {
                "Protocol": "HTTP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "msg",
                    "Settings": [
                        "\"HTTP reject\""
                    ]
                },
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000002"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1" 
                    ]
                }
            ]
        }
    ]
}

この状態で、EC2インスタンスからHTTP/80の通信をしようとしてみます。

$ curl -m 5 -I http://dev.classmethod.jp
curl: (56) Recv failure: Connection reset by peer

タイムアウトではなく、接続先からコネクションがリセットされたとメッセージが出力されました。まさにrejectです。一般利用者からしたら何らかの原因で拒否されたことが分かりやすいですね。

Wiresharkでこの時の通信を確認してみます。

HTTPのREJECT設定時のパケットキャプチャ

しっかりとRST + ACKがレスポンスとして返ってきていますね。なお、ICMPのパケットは特に発生していないようでした。

CloudWatch Logsに出力されたログを確認すると、以下のようなログが出力されていました。

{
    "firewall_name": "network-firewall",
    "availability_zone": "us-east-1a",
    "event_timestamp": "1678433301",
    "event": {
        "app_proto": "http",
        "src_ip": "10.1.1.37",
        "src_port": 49596,
        "event_type": "alert",
        "alert": {
            "severity": 3,
            "signature_id": 1000002,
            "rev": 1,
            "signature": "HTTP reject",
            "action": "blocked",
            "category": ""
        },
        "flow_id": 2156274843503677,
        "dest_ip": "13.248.175.13",
        "proto": "TCP",
        "http": {
            "hostname": "dev.classmethod.jp",
            "url": "/",
            "http_user_agent": "curl/7.88.1",
            "http_method": "HEAD",
            "protocol": "HTTP/1.1",
            "length": 0
        },
        "dest_port": 80,
        "timestamp": "2023-03-10T07:28:21.711137+0000"
    }
}

actionがdrop actionの時と同じくblockedでした。dropなのかrejectなのかはsignaturesignature_idから判断する必要があるようです。

ドメインリスト ルールグループの場合

次に、ドメインリスト ルールグループの場合を試します。

ドメインリストはALLOWLISTDENYLISTかの2択です。REJECTLISTのようなものはないので、恐らくrejectはできないと思いますが、試してみます。

定義したルールグループのルールは以下の通りです。HTTPでのdev.classmethod.jpに対して拒否するようにしています。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-domain-list \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "RulesSourceList": {
        "Targets": [
            "dev.classmethod.jp"
        ],
        "TargetTypes": [
            "HTTP_HOST"
        ],
        "GeneratedRulesType": "DENYLIST"
    }
}

この状態で、EC2インスタンスからdev.classmethod.jpに対して、HTTP/80の通信をしようとしてみます。

$ curl -m 5 -I http://dev.classmethod.jp
curl: (28) Operation timed out after 5000 milliseconds with 0 bytes received

接続断ではなく、タイムアウトとなりました。

Wiresharkでこの時の通信を確認してみます。

ドメインリストのDENYLIST設定時のパケットキャプチャ

先に検証したdrop actionの時と全く同じ挙動で、HEAD / HTTP/1.1のリクエストに対するレスポンスが返ってこず。最終的にはFIN + ACKをクライアントから投げていますね。

CloudWatch Logsに出力されたログを確認すると、以下のようなログが出力されていました。

{
    "firewall_name": "network-firewall",
    "availability_zone": "us-east-1a",
    "event_timestamp": "1678433702",
    "event": {
        "tx_id": 0,
        "app_proto": "http",
        "src_ip": "10.1.1.37",
        "src_port": 53886,
        "event_type": "alert",
        "alert": {
            "severity": 1,
            "signature_id": 1,
            "rev": 1,
            "signature": "matching HTTP denylisted FQDNs",
            "action": "blocked",
            "category": ""
        },
        "flow_id": 267369727812699,
        "dest_ip": "76.223.57.58",
        "proto": "TCP",
        "http": {
            "hostname": "dev.classmethod.jp",
            "url": "/",
            "http_user_agent": "curl/7.88.1",
            "http_method": "HEAD",
            "protocol": "HTTP/1.1",
            "length": 0
        },
        "dest_port": 80,
        "timestamp": "2023-03-10T07:35:02.160065+0000"
    }
}

Suricata互換のIPSルール ルールグループの場合

最後にSuricata互換のIPSルール ルールグループの場合を試します。

定義したルールグループのルールは以下の通りです。HTTPでのdev.classmethod.jpに対して拒否するようにしています。

aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-suricata \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "RulesString": "reject http $HOME_NET any -> $EXTERNAL_NET 80 (http.host; dotprefix; content:\"dev.classmethod.jp\"; endswith; msg:\"Reject HTTP domain\"; sid:1000011; rev:1;)"
}

この状態で、EC2インスタンスからdev.classmethod.jpに対して、HTTP/80の通信をしようとしてみます。

$ curl -m 5 -I http://dev.classmethod.jp
curl: (56) Recv failure: Connection reset by peer

タイムアウトではなく、接続がリセットされましたね。

Wiresharkでこの時の通信を確認してみます。

SuricataのHTTPのREJECT設定時のパケットキャプチャ

RST + ACKがレスポンスとして返ってきていますね。

CloudWatch Logsに出力されたログを確認すると、以下のようなログが出力されていました。

{
    "firewall_name": "network-firewall",
    "availability_zone": "us-east-1a",
    "event_timestamp": "1678434721",
    "event": {
        "tx_id": 0,
        "app_proto": "http",
        "src_ip": "10.1.1.37",
        "src_port": 49082,
        "event_type": "alert",
        "alert": {
            "severity": 3,
            "signature_id": 1000011,
            "rev": 1,
            "signature": "Reject HTTP domain",
            "action": "blocked",
            "category": ""
        },
        "flow_id": 979093120163954,
        "dest_ip": "76.223.57.58",
        "proto": "TCP",
        "http": {
            "hostname": "dev.classmethod.jp",
            "url": "/",
            "http_user_agent": "curl/7.88.1",
            "http_method": "HEAD",
            "protocol": "HTTP/1.1",
            "length": 0
        },
        "dest_port": 80,
        "timestamp": "2023-03-10T07:52:01.863940+0000"
    }
}

TCPに対する drop/reject actionの違い

drop actionの場合

先ほどはHTTP/80に対する drop/reject actionの違いを確認しました。

せっかくなので、TCP/80に対するdrop/reject actionも確認します。

まずはdrop actionからです。

定義したルールグループのルールは以下の通りです。TCP/80に対してdropするようにしています。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "DROP",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "msg",
                    "Settings": [
                        "\"TCP drop\""
                    ]
                },
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000001"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        }
    ]
}

この状態で、EC2インスタンスからHTTP/80の通信をしようとしてみます。

$ curl -I -m 5 http://dev.classmethod.jp
curl: (28) Connection timed out after 5000 milliseconds

タイムアウトになりましたね。

Wiresharkでこの時の通信を確認してみます。

TCPのDROP設定時のパケットキャプチャ

TCP/80に対するSYNのレスポンスがないことが分かります。

CloudWatch Logsに出力されたログを確認すると、以下のようなログが出力されていました。

{
    "firewall_name": "network-firewall",
    "availability_zone": "us-east-1a",
    "event_timestamp": "1678521174",
    "event": {
        "src_ip": "10.1.1.47",
        "src_port": 45102,
        "event_type": "alert",
        "alert": {
            "severity": 3,
            "signature_id": 1000001,
            "rev": 1,
            "signature": "TCP drop",
            "action": "blocked",
            "category": ""
        },
        "flow_id": 315433996915730,
        "dest_ip": "13.248.175.13",
        "proto": "TCP",
        "dest_port": 80,
        "timestamp": "2023-03-11T07:52:54.398354+0000"
    }
}

reject actionの場合

続いて、reject actionの場合です。

定義したルールグループのルールは以下の通りです。TCP/80に対してrejectするようにしています。

$ aws network-firewall describe-rule-group \
    --rule-group-name network-firewall-rule-group-5-tuple \
    --type STATEFUL \
    --query "RuleGroup.RulesSource"
{
    "StatefulRules": [
        {
            "Action": "REJECT",
            "Header": {
                "Protocol": "TCP",
                "Source": "$HOME_NET",
                "SourcePort": "ANY",
                "Direction": "FORWARD",
                "Destination": "$EXTERNAL_NET",
                "DestinationPort": "80"
            },
            "RuleOptions": [
                {
                    "Keyword": "msg",
                    "Settings": [
                        "\"TCP reject\""
                    ]
                },
                {
                    "Keyword": "sid",
                    "Settings": [
                        "1000004"
                    ]
                },
                {
                    "Keyword": "rev",
                    "Settings": [
                        "1"
                    ]
                }
            ]
        }
    ]
}

この状態で、EC2インスタンスからHTTP/80の通信をしようとしてみます。

$ curl -I -m 5 http://dev.classmethod.jp
curl: (7) Failed to connect to dev.classmethod.jp port 80 after 202 ms: Couldn't connect to server

202msでサーバーに接続できなかったと返してくれました。

Wiresharkでこの時の通信を確認してみます。

TCPのREJECT設定時のパケットキャプチャ

TCP/80に対するSYNのRST/ACKが返ってきていることが分かります。

CloudWatch Logsに出力されたログを確認すると、以下のようなログが出力されていました。

{
    "firewall_name": "network-firewall",
    "availability_zone": "us-east-1a",
    "event_timestamp": "1678521541",
    "event": {
        "src_ip": "10.1.1.47",
        "src_port": 32818,
        "event_type": "alert",
        "alert": {
            "severity": 3,
            "signature_id": 1000004,
            "rev": 1,
            "signature": "TCP reject",
            "action": "blocked",
            "category": ""
        },
        "flow_id": 1045219831571461,
        "dest_ip": "76.223.57.58",
        "proto": "TCP",
        "dest_port": 80,
        "timestamp": "2023-03-11T07:59:01.844805+0000"
    }
}

ポリシーに応じてdropとrejectを使い分けよう

AWS Network Firewall のステートフルルールグループが TCP トラフィックの reject action をサポートしたアップデートを紹介しました。

reject actionを設定することでタイムアウトまでわざわざ待つ必要がなくなります。

dropとrejectはポリシーに応じて使い分けましょう。

よくdropがrejectよりも良いというのは聞きますね。これは、以下のような理由かなと推測します。

  1. rejectはわざわざ送信元に通知してしまう関係上、宛先が存在していることを伝えることになるため、dropの方が良い
  2. dropの方がポートスキャンまでに時間がかかる

1つ目の理由はネットワーク型IPSが普及している現在、あまり関係ないかなと考えます。攻撃者もネットワーク型IPSの存在は考えていると推測します。

2つ目の理由についても攻撃者がポートスキャンする場合、元々タイムアウトまで悠長に待つこともないのかなと思います。

じゃあ、dropよりもrejectが良いのかと言うとそれも違います。

rejectにすることでRSTパケットが流れる分、ネットワークに流れる通信の流量は僅かに増えます。ここで、「タイムアウトになるまで通信を打ち続けたりするよりかは量が少ないのでは?」と思われるかもしれません。確かにそれも一理あるのですが、UDPリフレクションのように偽装したIPアドレスに対してRST/ACKパケットを大量に送りつけられる可能性もあります。RST/ACKのパケットサイズは小さいですが、攻撃者と思われる通信にわざわざ「通信できませんでした」と送り返す必要もないと考えます。

そのため、個人的には内部から外部への通信はrejectで、外部から内部への通信はdropみたいな考え方でも良いんじゃないかなと思います。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!