firewall: add default-policy to policy config v3 - #15950
Conversation
AppProtoToString(ALPROTO_HTTP1) returns "http", so an HTTP/1 policy had to be written as `http:` while its rule hooks were already spelled `http1:`. Use the same name in both places. Ticket: 8712
The policy config was a flat map mixing packet hooks and app-layer
protocols: `packet-filter` next to `dns`. There was no node that meant
"the packet hooks" or "the app-layer hooks", so a setting could not be
scoped to one group.
Move each group under its own node:
packet-filter -> packet.filter
packet-pre-flow -> packet.pre-flow
packet-pre-stream -> packet.pre-stream
<proto>.<hook> -> app.<proto>.<hook>
Ticket: 8712
Every hook has a built-in default policy, but expressing anything other
than the built-in meant naming each hook explicitly. Add a
`default-policy` setting that covers all hooks below it, so unlisted
hooks still get a policy. For any hook the most specific setting present
wins:
app.<proto>.<sub state>.<hook>
app.<proto>.<sub state>.default-policy
app.<proto>.default-policy
app.default-policy
default-policy
built-in
The packet hooks follow the same pattern under `packet`.
Resolution moves into ResolveFirewallPolicy(), which walks the candidate
paths most-specific-first and stops at the first one that is configured.
A path that is present but empty is now a startup error rather than being
treated as unset.
DoParseAppSubStatePolicy() collapses into DoParseAppPolicy() as a sub state
hook only differs by an extra path segment. Path assembly and hook-name
normalisation move to helpers now that both are needed in more places.
Ticket: 8712
Validate the resolved scope against the class of hook it is being applied to and fail at startup if it does not fit, naming the config path and the scopes that would be accepted there. A global `accept:tx` is now a startup error. Ticket: 8712
There was a problem hiding this comment.
Pull request overview
This PR updates Suricata’s experimental firewall policy configuration to support hierarchical default-policy inheritance and to validate action scopes against the hook class they apply to, while also restructuring the YAML layout to separate packet hooks from app-layer hooks.
Changes:
- Introduces multi-level
default-policyresolution (most-specific match wins) across global, packet, and app protocol/sub-state policy nodes. - Validates action scopes so incompatible scopes (e.g.,
accept:txapplied to packet hooks) fail at startup instead of being applied silently. - Updates example configuration and user guide documentation to reflect the new
packet.*andapp.*policy structure.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| suricata.yaml.in | Updates commented example config to show hierarchical default-policy and the new packet/app nodes. |
| src/detect.h | Adds a firewall policy “class” enum to support class-specific validation. |
| src/detect-parse.c | Implements hierarchical policy resolution, scope validation, and new packet/app policy loading logic. |
| doc/userguide/firewall/firewall-example.rst | Updates the HTTP example config snippet to use policies.app.http1.*. |
| doc/userguide/firewall/firewall-design.rst | Documents the new config structure, precedence rules, and scope-validity constraints. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #15950 +/- ##
==========================================
- Coverage 83.03% 82.99% -0.04%
==========================================
Files 1003 1003
Lines 276582 276602 +20
==========================================
- Hits 229647 229553 -94
- Misses 46935 47049 +114
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
Information: QA ran without warnings. Pipeline = 32734 |
| } | ||
|
|
||
| const char *app_name = AppProtoToString(app_proto); | ||
| const char *app_name = (app_proto == ALPROTO_HTTP1) ? "http1" : AppProtoToString(app_proto); |
There was a problem hiding this comment.
Maybe time to have another function than AppProtoToString that does this
And use it in DetectRegisterAppLayerHookLists and DetectEngineAppHookToSmlist where we already do this
| FatalError("internal error: failed to assemble firewall policy config string"); | ||
| char scope[256]; | ||
| if (sub_state_name != NULL) { | ||
| char sub[64]; |
There was a problem hiding this comment.
I would like to see how this magic 64 is computed ;-)
jufajardini
left a comment
There was a problem hiding this comment.
Added some minor comments, basically related to documentation.
|
|
||
| If applayer is available, rules from the following tables apply. The tables for the | ||
| application layer are per app layer protocol and per protocol state. e.g. ``http:request_line``. | ||
| application layer are per app layer protocol and per protocol state. e.g. ``http1:request_line``. |
There was a problem hiding this comment.
To be confirmed, but if we already have stuff backported where http is an alias to http1, should we keep it that way? (or do we change in 9, and document?)
| filter: ["drop:packet"] | ||
| pre-flow: ["accept:hook"] | ||
| pre-stream: ["accept:hook"] | ||
| app: |
There was a problem hiding this comment.
Maybe nitty, but as far as naming goes, I prefer proto instead of app.
| return -1; | ||
| } | ||
| if (!FirewallScopeValidForClass(tmp.action_scope, pol_class)) { | ||
| char hint[32]; |
There was a problem hiding this comment.
Minor, but maybe a comment on why 32 here, in case something changes ppl know what to take into account? Or in the fn...
Follow-up of #15944
Add a
default-policysetting that applies to all hooks below it. For any hook the most specific setting present wins.The flat layout had no way to express a default covering only the packet hooks or only the app hooks, so both move under their own node:
packet-filterbecomespacket.filterand<proto>becomesapp.<proto>.Action scopes are now validated against the hook they reach. A global
accept:txarriving at a packet hook is a startup error instead of being applied silently.Link to ticket: https://redmine.openinfosecfoundation.org/issues/8712
Describe changes:
v3:
v1/v2:
SV_BRANCH=OISF/suricata-verify#3252
To consider:
As we changed the config structure for default policies, we can consider adding upgrade checks.
But since the firewall mode is experimental in 8, I avoided any conversion checks from the previous versions to keep the code simpler.