
クロスインダストリー第1本部の京極です。
皆さん、AWS Network Firewallは使っていますか! 最近は、Transit GatewayとAWS Network Firewallをよく触る機会に恵まれている私ですが、とあるプロジェクトでAWS Network Firewallルール形式の変更を検討する機会がありました。
今回は AWS Network Firewallのルールを標準形式ルールからSuricata互換形式への切り替えにおいて、
Suricata互換形式の使い方
標準形式ルールからの切り替え
の2つを検証してみましたのでご紹介します!
この記事で書いていること
- Suricata互換形式の使い方
- AWS Network Firewallのドメインリストルールグループ を Suricata互換形式ルールグループ に切り替えるやり方
本記事では、以降 AWS Network Firewall = NFW と表記している箇所があります。
前提となる単語
本記事のNFW用語は以下のように使い分けています。
| No | 用語 | 詳細な説明 |
|---|---|---|
| 1 | ルール(Rule) | パケットを検査する際の個々の一致条件(プロトコル・送信元/宛先IP・ポート・ドメイン名・Suricataルールオプションなど)と、一致した場合のアクション(pass/drop/reject/alert)を定義した最小単位 |
| 2 | ルールグループ(Rule group) | 1つ以上のルールをまとめた、再利用可能なセット。単体では使えず、ファイアウォールポリシーに参照させることで初めて有効になる。ステートレス/ステートフルの2種類があり、同じルールグループを複数のポリシーから参照できる |
何故切り替えようと思ったの?
私はこれまでNFWの構築や運用において「Suricata互換形式ルール」を触ったことがありませんでした。
そんな私が、何故NFWのルールをSuricata互換形式に置き換えようと思ったのかというと… ずばり、ドメインリストルールグループが使いづらい!と感じたからです!
標準形式で作られたドメインリストルールグループは、どうしても痒いところに手が届かない仕様となっています。
私が不便だと思った点を2つほど挙げてみました。
不便と思った点 ①
送信元CIDRごとの許可ルールを作りたければルールグループが肥大化していく点です。
ドメインリストのルールグループでは、
- ドメインリストグループに記載したドメイン名
- 送信元IP
- アクション
の3つを以ってルールを定義しますが、
例えば、 1に、
- example.com
- sabawaku.com
2に
- 172.16.0.0/16
- 192.168.0.0/24
3に
- Pass
を定義した場合、
- 172.16.0.0/16 → example.com が Passされる
- 172.16.0.0/16 → sabawaku.com が Passされる
- 192.168.0.0/24 → example.com が Passされる
- 192.168.0.0/24 → sabawaku.com が Passされる
の4つに対してアクションを行います。
こうなると、
- 172.16.0.0/16 には example.com
- 192.168.0.0/24 には sabawaku.com だけ許可したい!
といった制御を行うためには、ルールグループを分けなければなりません。
不便と思った点 ②
1つのルールグループで指定できるアクションが1つしかない。
NFWでは、Passルールにヒットしたログが出力されません。 なので、Passルールにヒットしたログを出力したければ、Passルールの前にAlertログを差し込む必要があります。
つまり、1ルールグループの中でPassとAlertの2つのアクションをもったルールを一つずつ作成する必要があります。
ところが、標準のドメインリストルールでは、下図のように指定できるアクションが1つしかありません。

つまり、1つのルールグループの中にPassルールとAlertルールの両方を書くことができません。
5-Tuple 形式のルール(標準形式ルール)であれば、1つのルールグループの中に複数のアクションが記載できるため、上記のような不満はありません。 ただ、ドメインリストだけはどうしても扱いづらい点が出てしまうのです。
Suricata互換形式ルールとは?
Suricata互換形式については、過去に弊社の別の方がブログにしています。 https://blog.serverworks.co.jp/aws-network-firewall-suricata
AWS Network Firewallでは、ルールグループを作成する際に、以下の3つの形式が選択できます。
| No | 形式 | 詳細な説明 |
|---|---|---|
| 1 | 標準ステートフルルール(Standard stateful rule groups) | プロトコル・送信元/宛先IP・送信元/宛先ポート・トラフィック方向(Any/Forward)などの5-tuple情報をGUIフォームで指定するシンプルな形式。ドメイン名は扱えず、IP/ポートベースの通信制御に向く |
| 2 | ドメインリスト(Domain list) | 制御したいドメイン名(完全一致 or .example.comのようなワイルドカード)と、対象プロトコル(HTTP/HTTPS)、アクション(Allow/Deny)をGUIフォームで指定する形式。HTTPSはTLSのSNI、HTTPはHostヘッダで判定する |
| 3 | Suricata互換ルール文字列(Suricata compatible rule strings) | Suricataのルール構文をそのまま記述する形式。pass/drop/reject/alertを自由に組み合わせられ、標準のドメインリスト形式にはないマッチ条件も使える。 |
NFWでは、No.3 の 『Suricata互換形式でのルール』記載が可能となっています。
Suricata互換形式のメリットは、
- ルールオプションを完全に制御できる
- ルールがテキストベースなので、コピー・編集・共有・バックアップがしやすい
- ドメインリスト形式にはない検査条件が使える
- pass/drop/reject/alertを自由に組み合わせた細かいルール設計ができる
一方でデメリットは、
- 学習コストが上がる
- GUIのフォーム入力から、Suricata構文の手書きに変わるため、sidの重複管理、flow:establishedの書き忘れによる意図しない通信断など、ヒューマンエラーを誘発しやすくなる
- 記述・レビューの手間が増える:GUIなら数クリックで済むドメイン追加が、ルール文字列の追記・capacity再確認などの作業になる
などが考えられます。
こう見ると、学習コストを除いてデメリットらしいデメリットは特にありません。 IaCによる運用と、AIを活用したレビューなど、デプロイ前のチェックを怠らなければSuricata互換を避ける理由があまりないと感じます。
AWSとしての推奨は?
公式ドキュメントとして、どちらの形式がよい、ということをAWSは公表していません。 ただ、公式ブログには以下のような記載があります。
Creating the same rule using Suricata compatible rule strings The console rule builder is a quick way to get started, but we recommend using Suricata compatible rule strings for production deployments. Suricata rules give you full control over rule options, make rules straightforward to copy, edit, share, and back up, and support the majority of the Suricata engine. For more information, see Limitations and caveats for stateful rules in AWS Network Firewall.
訳) コンソールのルールビルダーは手軽に始められる方法ですが、本番環境へのデプロイにはSuricata互換のルール文字列を使用することをお勧めします。Suricataルールを使用すると、ルールオプションを完全に制御でき、ルールのコピー、編集、共有、バックアップが容易になり、Suricataエンジンの大部分がサポートされます。詳細については、AWS Network Firewallのステートフルルールの制限事項と注意事項を参照してください。
- 標準形式の設定は直感的であるが、細かな設定制御ができず
- Suricata互換形式の設定は複雑であるが、細かな制御ができる
という点。
そして、形式を後からコンバートできないという観点から、柔軟な設定を求められる可能性の高い本番環境ではあらかじめSuricata互換形式を採用しておくとよい、という風に解釈できます。
検証
さて、前置きが長くなりましたがサクッと検証しましょう。 検証したい内容は以下2つです。
- 検証1. Suricata互換形式のルールが動作するか
- 検証2. 標準形式ルールをSuricata互換形式のルールに置き換えられるか
構成図
検証で使用した環境です。
今回は、ドメインリストルールをSuricata互換形式に置き換える、という検証であるため非常にシンプルな構成です。

なお、重要なことですが、 NFWのルールの評価順が「STRICT_ORDER」の設定となっていることを前提とします。
検証シナリオ
AWS Management Consoleから作成した標準のドメインリストルールグループ「 nfw-rule-group-domainlist-vpc-to-internet 」があります。
この環境のNFWではホワイトリスト形式を採用しており、httpとhttpsプロトコルの両方で、以下のドメインへの通信を許可(Pass)しています。
- .google.com
この環境で、Passルールにhitした際のログを出力したいという要件がでてきました。 NFWでは、この要件を満たすために「Alertルール」の作成が必要となります。
標準のドメインリストルールグループでは、「Passルール」しか指定ができず、「ログ出力用のAlertルール」を作成することができません。 この運用が不便だと感じた管理者は、このルールをSuricata互換形式ルールに置き換えることにしました。
検証1:Suricata互換形式のルールが動作するか
1.NFWのルールグループを新規追加します。
※本検証は、GUIから作成していますが、実際のデプロイや管理はCloudFormationの使用をお奨め致します。
2.作成するルールは以下の通りです。
.google.com を許可
これに対応するルールを作成します。 標準形式ルールをいきなりSuricata互換形式に置き換えるのは怖いので、まずは通信を制御しないルールを作成してみます。
alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; tls.sni; dotprefix; content:".google.com"; endswith; nocase; sid:101; rev:2;) alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; http.host; dotprefix; content:".google.com"; endswith; sid:103; rev:2;)
差し込むルールはこれです。 一見すると、「うっ」となるような感じかもしれませんが、書いている内容はさほど難しくはありません。
alert tls $HOME_NET any -> $EXTERNAL_NET any (オプション群)
となっています。指定した内容やオプションは以下の通りです。
ルールヘッダ
| 要素 | 値 | 説明 |
|---|---|---|
| action | alert |
マッチしたらログ出力。通信はブロックしない。評価は後続ルールに継続する |
| protocol | tls |
TLS プロトコルに限定(TLS ハンドシェイクを解析対象とする) |
| 送信元 | $HOME_NET any |
HOME_NET(RuleVariables で定義した IP 範囲)の任意ポートから |
| 方向 | -> |
一方向(クライアント→サーバー) |
| 宛先 | $EXTERNAL_NET any |
EXTERNAL_NET の任意ポートへ |
ルールオプション
| オプション | 値 | 説明 |
|---|---|---|
msg |
"Domain allow rule for .google.com" |
ログに出力されるメッセージ文字列。人が読むための説明 |
flow |
to_server, established |
to_server: クライアント→サーバー方向のパケットのみ対象。established: TCP 3-way handshake が完了済みのセッションのみ対象 |
tls.sni |
(値なし) | 後続の content マッチ対象を TLS Client Hello の SNI(Server Name Indication)フィールドに限定するスティッキーバッファ |
dotprefix |
(値なし) | content の先頭にドット . を自動付与する。サブドメインのワイルドカードマッチを実現する |
content |
".google.com" |
マッチさせたい文字列。tls.sni バッファ内でこの文字列を探す |
endswith |
(値なし) | content がバッファの末尾に一致する場合のみマッチ。部分一致を防ぐ |
nocase |
(値なし) | 大文字・小文字を区別しない |
sid |
101 |
ルールの一意識別子(Signature ID)。ルールグループ内で重複不可 |
rev |
2 |
ルールのリビジョン番号。同一 sid のルール更新時にインクリメントする |
GUIで作成するときは、いつもの「標準ルール」ではなく、「Suricata互換ルール文字列」を選びます。

実際にルールを記載します。

次に、EC2からhttps://www.google.com へアクセスしてみます。 CloudWatch にalertログが出力されました。出力されたログは以下のような感じです。
※CloudWatch Logsへ出力されたNFW alertログを抜粋
{
"firewall_name": "anfw-suricata-verify",
"availability_zone": "ap-northeast-1a",
"event_timestamp": "1786958454",
"event": {
"aws_category": "",
"tx_id": 0,
"app_proto": "tls",
"ip_v": 4,
"src_ip": "10.1.0.7",
"src_port": 56008,
"event_type": "alert",
"alert": {
"severity": 3,
"signature_id": 101,
"rev": 2,
"signature": "Domain allow rule for .google.com",
"action": "allowed",
"category": ""
},
"ts_progress": "client_hello_done",
"flow_id": 1929833655864901,
"dest_ip": "142.251.156.119",
"proto": "TCP",
"verdict": {
"action": "pass"
},
"tls": {
"sni": "www.google.com",
"version": "TLS 1.3",
"ja4": "t13d3514h2_bfa337485184_97a66a8f4cb1",
"client_alpns": [
"h2",
"http/1.1"
]
},
"tc_progress": "server_in_progress",
"dest_port": 443,
"pkt_src": "geneve encapsulation",
"aws_metadata": {
"resource_arn": "arn:aws:network-firewall:ap-northeast-1:724542698819:stateful-rulegroup/nfw-rule-group-domainlist-vpc-to-internet-suricata"
},
"timestamp": "2026-08-17T09:20:54.719373+0000",
"direction": "to_server"
}
}
www.goolge.com の通信を拾っていますね。 ここで注目してほしいのは、"signature_id": 101 の部分です。
これが先ほど作った
alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; tls.sni; dotprefix; content:".google.com"; endswith; nocase; sid:101; rev:2;)
このルールのことです。sid 101 なので、ちゃんとこのルールにヒットしていますね!
では、httpの方はどうでしょうか? EC2からhttp://www.google.com へアクセスしてみます。
これもCloudWatch Logsにalertログが出力されました。出力されたログは以下のような感じです。
※CloudWatch Logsへ出力されたNFW alertログを抜粋
{
"firewall_name": "anfw-suricata-verify",
"availability_zone": "ap-northeast-1a",
"event_timestamp": "1786959100",
"event": {
"aws_category": "",
"tx_id": 0,
"app_proto": "http",
"ip_v": 4,
"src_ip": "10.1.0.7",
"src_port": 53212,
"event_type": "alert",
"alert": {
"severity": 3,
"signature_id": 103,
"rev": 2,
"signature": "Domain allow rule for .google.com",
"action": "allowed",
"category": ""
},
"ts_progress": "request_complete",
"flow_id": 1164666496575304,
"dest_ip": "142.251.150.119",
"proto": "TCP",
"verdict": {
"action": "pass"
},
"http": {
"hostname": "www.google.com",
"url": "/",
"http_user_agent": "curl/8.17.0",
"http_method": "GET",
"protocol": "HTTP/1.1",
"length": 0
},
"tc_progress": "response_started",
"dest_port": 80,
"pkt_src": "geneve encapsulation",
"aws_metadata": {
"resource_arn": "arn:aws:network-firewall:ap-northeast-1:724542698819:stateful-rulegroup/nfw-rule-group-domainlist-vpc-to-internet-suricata"
},
"timestamp": "2026-08-17T09:31:40.931582+0000",
"direction": "to_server"
}
}
Suricataで記載したgoogle.comへの通信は、「alert」のみであり、通信を制御しません。 しかし、実際はcurl- v のコマンドが通っているので、通信は「pass」されている、ということになります。
- 評価順1:Suricata互換形式ルール → alertルールによるログ出力のみ
- 評価順2:標準形式ルール → passルールにより通信の許可

なので、想定通り、通信は上記のように評価されているということがわかります。
検証1は、想定通りに動きました。
検証2:Suricata互換形式ルールに差し替えてみる
では、次に「標準形式ルール」を「Suricata互換形式ルール」に差し替えてみましょう。
差し替え前の確認事項として重要なのは、 alert ルールにきちんとヒットしていたかどうか です。
1.Suricata互換形式を編集する。
先頭に記載されているアクション「alert」を「pass」に変えるだけです。 アクションをalert → passへ変更してあげることでIDS(Intrusion Detection System) としての動作から IPS(Intrusion Prevention System) としての動作へと切り替わります。
before
alert tls $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; tls.sni; dotprefix; content:".google.com"; endswith; nocase; sid:101; rev:2;) alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; http.host; dotprefix; content:".google.com"; endswith; sid:103; rev:2;)
after
pass tls $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; tls.sni; dotprefix; content:".google.com"; endswith; nocase; sid:101; rev:2;) alert http $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; http.host; dotprefix; content:".google.com"; endswith; sid:103; rev:2;)
※ここでは「1ルールグループに複数のアクションを指定できる」ということを確認するためにあえて、http側のルール(sid:103)はalertのままで置いておきます。
2.標準形式ルールを削除する
ファイアーウォールポリシーから、標準形式ルールの関連付けを解除します。 すると、Suricata互換形式で記載したルールグループだけが残ります。

この状態になると、いよいよ「Suricata互換形式」ルールでしかパケットは評価されなくなります。 つまり、この状態でhttps://www.google.com への通信が通ればOKです。 コマンド実行結果は下記のような形になりました。(Passルールはログを出さないため、コマンド実行結果にて確認)
[ec2-user@ip-10-1-0-5 ~]$ curl -I https://www.google.com HTTP/2 200 content-type: text/html; charset=ISO-8859-1 content-security-policy-report-only: object-src 'none';base-uri 'self';script-src 'nonce-fe63qUObAm8VB_d3RVC9gQ' 'strict-dynamic' 'report-sample' 'unsafe-eval' 'unsafe-inline' https: http:;report-uri https://csp.withgoogle.com/csp/gws/other-hp
きちんとつながっていますね。
状態としては以下のような感じです。

では、アクションをPassにしなかったsid:103のルールはどうでしょうか? これは、http://www.google.co.jp にアクセスしたときにhitするルールです。
CloudWatch Logs上では以下のようなメッセージが出ていました。
{
"firewall_name": "anfw-suricata-verify",
"availability_zone": "ap-northeast-1a",
"event_timestamp": "1787033498",
"event": {
"aws_category": "",
"tx_id": 0,
"app_proto": "http",
"ip_v": 4,
"src_ip": "10.1.0.5",
"src_port": 34904,
"event_type": "alert",
"alert": {
"severity": 3,
"signature_id": 103,
"rev": 2,
"signature": "Domain allow rule for .google.com",
"action": "allowed",
"category": ""
},
"ts_progress": "request_complete",
"flow_id": 753233885134794,
"dest_ip": "142.251.152.119",
"proto": "TCP",
"verdict": {
"action": "drop" ← Passルールがないため、dropされている
},
"http": {
"hostname": "www.google.com",
"url": "/",
"http_user_agent": "curl/8.17.0",
"http_method": "HEAD",
"protocol": "HTTP/1.1",
"length": 0
},
"tc_progress": "response_started",
"dest_port": 80,
"pkt_src": "geneve encapsulation",
"aws_metadata": {
"resource_arn": "arn:aws:network-firewall:ap-northeast-1:724542698819:stateful-rulegroup/nfw-rule-group-domainlist-vpc-to-internet-suricata"
},
"timestamp": "2026-08-18T06:11:38.512478+0000",
"direction": "to_server"
}
Passルールがないため、dropされています。ちゃんと想定通りの挙動となっています。↓

切替も出来ていますし、1ルールグループの中に「Alert」と「Pass」の両方のアクションルールが書けています!
補足:passルールのオプションでログ出力も可能
Suricata互換形式は、標準形式ルールより柔軟性に富んでいます。
Network Firewallでは、通常「Pass」ルールにヒットしたログは出力しない という大前提があります。 標準形式でPassルールのログを出力するには、Passルールの前に、対応したAlertルールを書く必要があります。
ところが、Suricata互換形式では、
pass tls $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; tls.sni; dotprefix; content:".google.com"; endswith; nocase; alert; sid:101; rev:2;)
pass http $HOME_NET any -> $EXTERNAL_NET any (msg:"Domain allow rule for .google.com"; flow:to_server, established; http.host; dotprefix; content:".google.com"; endswith; alert; sid:102; rev:2;)
このようにalertオプションを入れておくことで、passルールであってもログを出力させることができます。 わざわざpassルールヒット用alertログルールを用意しなくても、passルールの中でオプションとしてログ出力の有無を制御できるということです。
つまり、今回だと確認用にalertルールを記載した上で、後からpassルールに変更しましたが、 最初からpassルールを書いて、alertログを出力することも可能です。
まとめ
標準形式ルールからSuricata互換形式ルールに変更する際は、
- 標準形式ルールグループと同じ内容で、アクションが「alert」のSuricata互換形式のルールを作成する
- 標準形式ルールグループの「前に」Suricata互換形式ルールグループを置く
- alertログから、対象ドメインのルールにhitしていることを確認する
- 一定の試験期間を確保し、alertログにてSuricata互換形式ルールが正常にhitしていることが確認できたら、標準形式ルールを「Firewallポリシー」の関連付けから解除する
- リリース後、動作に問題がなければ、関連付けを解除していた標準形式ルールを削除する
とすることで、比較的スムーズに移行ができるのではないかと思います。
記載方法が難しいSuricata互換形式ルールですが、きちんと使えれば「1つのルールグループで複数のアクション」を記載することができ、ルールグループの肥大化を抑止することができます。 また、トラブルシュートの際に必要な「Passルールのログ取得」も実装がしやすいです。
学習コストというハードルはあるものの、AI活用が活発化している現在ではそのハードルも下がっています。 Suricata互換形式で出来ることを考えると、利用の価値は大いにあります。
補足
Suricata互換形式の記載は独特なため、苦手な方は「Suricata Generator」というGUIでルールを作成できるツールが、github上で公開されていますので、利用を検討してみてもよいかもしれません。
github.com