* [PROPOSAL] New wiki page: Simple rule management with JSON
@ 2025-11-06 9:29 Alexandre Knecht
2025-11-06 23:15 ` Florian Westphal
0 siblings, 1 reply; 2+ messages in thread
From: Alexandre Knecht @ 2025-11-06 9:29 UTC (permalink / raw)
To: netfilter
[-- Attachment #1: Type: text/plain, Size: 2113 bytes --]
Hello,
I would like to propose a new wiki page that provides user-friendly
documentation
for nftables JSON rule management. This complements the technical reference in
libnftables-json(5) with practical examples and beginner-friendly explanations.
## Background
While implementing JSON-based nftables automation, I found that the existing
documentation, while technically accurate, lacks practical examples and clear
explanations of:
1. The difference between explicit commands ({"add": {"rule": ...}}) and
implicit format ({"rule": ...})
2. The semantics of ADD vs INSERT with handle positioning
3. Export/import compatibility considerations
4. Common expression and statement patterns in JSON
## Proposed Page
The proposed page "Simple rule management with JSON" would mirror the structure
of the existing "Simple rule management" page but focus on JSON API usage.
**Key sections:**
- Understanding JSON command structure (explicit vs implicit)
- Basic operations (add, insert, delete, replace, flush)
- Rule positioning with handles (ADD after, INSERT before)
- Common expressions and statements in JSON format
- Export/import compatibility
- Complete firewall example
- Debugging tips
The full content is available at:
[Attach: json_rule_management_wiki.md]
## Benefit
This documentation would:
- Lower the barrier to entry for JSON API users
- Complement the technical reference with practical examples
- Clarify common confusion points (especially handle positioning)
- Provide copy-paste examples for common use cases
I'm happy to work with wiki maintainers to refine the content and format it
appropriately for the wiki.
## Related Patches
This proposal accompanies two patches:
- [nf,v3] parser_json: support handle for rule positioning in JSON add
rule - https://patchwork.ozlabs.org/project/netfilter-devel/list/?series=481124
- [nf,v3,2/2] doc: clarify JSON rule positioning with handle field -
https://patchwork.ozlabs.org/project/netfilter-devel/list/?series=481127
These patches implement and document handle-based rule positioning in
the JSON API.
Best regards,
Alexandre Knecht
[-- Attachment #2: json_rule_management_wiki.md --]
[-- Type: text/markdown, Size: 11205 bytes --]
# Simple rule management with JSON
This page explains how to manage nftables rules using the JSON API. The JSON API provides a programmatic interface to nftables, which is especially useful for automation and integration with other tools.
## Understanding JSON command structure
The nftables JSON API has two formats:
### Explicit commands (for rule manipulation)
```json
{"nftables": [{"add": {"rule": {...}}}]}
{"nftables": [{"insert": {"rule": {...}}}]}
{"nftables": [{"delete": {"rule": {...}}}]}
{"nftables": [{"replace": {"rule": {...}}}]}
```
### Implicit format (for export/import)
```json
{"nftables": [{"rule": {...}}]}
```
**IMPORTANT**: The command wrapper (`"add"`, `"insert"`, etc.) matters! It determines how the rule is processed.
## Basic rule structure
A JSON rule has this basic structure:
```json
{
"rule": {
"family": "ip",
"table": "filter",
"chain": "output",
"expr": [
// expressions (match criteria)
// statements (actions)
]
}
}
```
## Listing rules with handles
To see rule handles in JSON format:
```bash
nft -j -a list ruleset
```
The `-a` flag includes handles, which you'll need for positioning, replacing, or deleting rules.
Example output:
```json
{
"nftables": [
{
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"handle": 5,
"expr": [
{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 22}},
{"accept": null}
]
}
}
]
}
```
## Adding rules (append to end)
**Command**: `"add"`
**Behavior**: Appends rule to the end of the chain
```bash
nft -j -f - << 'EOF'
{
"nftables": [
{
"add": {
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"expr": [
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "tcp", "field": "dport"}},
"right": 80
}
},
{"accept": null}
]
}
}
}
]
}
EOF
```
## Inserting rules (prepend to beginning)
**Command**: `"insert"`
**Behavior**: Inserts rule at the beginning of the chain
```bash
nft -j -f - << 'EOF'
{
"nftables": [
{
"insert": {
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"expr": [
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "tcp", "field": "dport"}},
"right": 443
}
},
{"accept": null}
]
}
}
}
]
}
EOF
```
## Positioning rules with handles
You can position rules relative to existing rules using the `"handle"` field.
### Add rule AFTER a specific handle
**Command**: `"add"` with `"handle"`
**Behavior**: Inserts rule immediately after the specified handle
```bash
# Suppose handle 5 exists, this adds a rule after it
nft -j -f - << 'EOF'
{
"nftables": [
{
"add": {
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"handle": 5,
"expr": [
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "tcp", "field": "dport"}},
"right": 8080
}
},
{"accept": null}
]
}
}
}
]
}
EOF
```
**Result**: New rule is inserted after handle 5
### Insert rule BEFORE a specific handle
**Command**: `"insert"` with `"handle"`
**Behavior**: Inserts rule immediately before the specified handle
```bash
# This inserts a rule before handle 5
nft -j -f - << 'EOF'
{
"nftables": [
{
"insert": {
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"handle": 5,
"expr": [
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "tcp", "field": "dport"}},
"right": 9090
}
},
{"accept": null}
]
}
}
}
]
}
EOF
```
**Result**: New rule is inserted before handle 5
### Important note about positioning
When you add multiple rules at the same handle position, they are always positioned relative to the **original handle**, not the previously inserted rule.
Example:
```
Initial: [rule1(h:2), rule2(h:3), rule3(h:4)]
Add rule A after handle 2:
Result: [rule1(h:2), ruleA, rule2(h:3), rule3(h:4)]
Add rule B after handle 2 again:
Result: [rule1(h:2), ruleB, ruleA, rule2(h:3), rule3(h:4)]
```
Rule B is inserted after the original handle 2 (rule1), not after rule A.
## Deleting rules
**Command**: `"delete"` with `"handle"`
**Behavior**: Removes the rule with the specified handle
```bash
nft -j -f - << 'EOF'
{
"nftables": [
{
"delete": {
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"handle": 5
}
}
}
]
}
EOF
```
**Note**: You can only delete by handle. You cannot delete by matching rule content in JSON.
## Replacing rules
**Command**: `"replace"` with `"handle"`
**Behavior**: Replaces the rule at the specified handle with new content
```bash
nft -j -f - << 'EOF'
{
"nftables": [
{
"replace": {
"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"handle": 5,
"expr": [
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "tcp", "field": "dport"}},
"right": 443
}
},
{"drop": null}
]
}
}
}
]
}
EOF
```
The rule at handle 5 is replaced with the new rule. The handle number remains the same.
## Flushing chains
Remove all rules from a chain:
```bash
nft -j -f - << 'EOF'
{
"nftables": [
{
"flush": {
"chain": {
"family": "inet",
"table": "filter",
"name": "input"
}
}
}
]
}
EOF
```
## Common expressions in JSON
### Match TCP destination port
```json
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "tcp", "field": "dport"}},
"right": 80
}
}
```
### Match IP source address
```json
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "ip", "field": "saddr"}},
"right": "192.168.1.100"
}
}
```
### Match IP address range
```json
{
"match": {
"op": "==",
"left": {"payload": {"protocol": "ip", "field": "daddr"}},
"right": {"range": ["192.168.1.1", "192.168.1.254"]}
}
}
```
### Match connection state
```json
{
"match": {
"op": "in",
"left": {"ct": {"key": "state"}},
"right": ["established", "related"]
}
}
```
## Common statements (actions) in JSON
### Accept
```json
{"accept": null}
```
### Drop
```json
{"drop": null}
```
### Counter
```json
{"counter": {"packets": 0, "bytes": 0}}
```
Or just:
```json
{"counter": null}
```
### Log
```json
{"log": {"prefix": "Firewall: "}}
```
### Reject
```json
{"reject": null}
```
### Jump to another chain
```json
{"jump": {"target": "chain-name"}}
```
## Export/Import compatibility
The implicit format (without command wrappers) is used for export/import:
```bash
# Export
nft -j list ruleset > rules.json
# Import
nft -j -f rules.json
```
**IMPORTANT**: In the implicit format, handles in rules are **ignored** during import. This ensures that exported rulesets can be imported into different systems where handle numbers may differ.
Example implicit format:
```json
{
"nftables": [
{"table": {"family": "inet", "name": "filter"}},
{"chain": {"family": "inet", "table": "filter", "name": "input"}},
{"rule": {
"family": "inet",
"table": "filter",
"chain": "input",
"handle": 999,
"expr": [
{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 22}},
{"accept": null}
]
}}
]
}
```
When imported, the `"handle": 999` is ignored, and a new handle is assigned automatically.
## Complete example: Building a firewall
```bash
nft -j -f - << 'EOF'
{
"nftables": [
{"flush": {"ruleset": null}},
{"add": {"table": {"family": "inet", "name": "filter"}}},
{"add": {"chain": {"family": "inet", "table": "filter", "name": "input", "type": "filter", "hook": "input", "prio": 0, "policy": "drop"}}},
{"add": {"rule": {"family": "inet", "table": "filter", "chain": "input", "expr": [
{"match": {"op": "in", "left": {"ct": {"key": "state"}}, "right": ["established", "related"]}},
{"accept": null}
]}}},
{"add": {"rule": {"family": "inet", "table": "filter", "chain": "input", "expr": [
{"match": {"op": "==", "left": {"meta": {"key": "iifname"}}, "right": "lo"}},
{"accept": null}
]}}},
{"add": {"rule": {"family": "inet", "table": "filter", "chain": "input", "expr": [
{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 22}},
{"accept": null}
]}}},
{"add": {"rule": {"family": "inet", "table": "filter", "chain": "input", "expr": [
{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 80}},
{"accept": null}
]}}},
{"add": {"rule": {"family": "inet", "table": "filter", "chain": "input", "expr": [
{"match": {"op": "==", "left": {"payload": {"protocol": "tcp", "field": "dport"}}, "right": 443}},
{"accept": null}
]}}}
]
}
EOF
```
This creates a basic firewall that:
1. Flushes existing ruleset
2. Creates table "filter"
3. Creates chain "input" with default policy DROP
4. Allows established/related connections
5. Allows loopback traffic
6. Allows SSH (port 22)
7. Allows HTTP (port 80)
8. Allows HTTPS (port 443)
## Debugging JSON
To see what JSON nftables generates from CLI commands:
```bash
# Run a command and see the JSON equivalent
nft -j add rule inet filter input tcp dport 22 accept
```
This helps you learn the JSON syntax by seeing how CLI commands translate to JSON.
## Summary of commands
| Operation | Command | With Handle | Behavior |
|-----------|---------|-------------|----------|
| Append to end | `"add"` | No | Adds rule at end of chain |
| Add after rule | `"add"` | Yes | Adds rule after specified handle |
| Prepend to beginning | `"insert"` | No | Adds rule at beginning of chain |
| Insert before rule | `"insert"` | Yes | Adds rule before specified handle |
| Delete rule | `"delete"` | Yes (required) | Removes rule by handle |
| Replace rule | `"replace"` | Yes (required) | Replaces rule at handle |
| Flush chain | `"flush"` | No | Removes all rules from chain |
## See also
- [Simple rule management](https://wiki.nftables.org/wiki-nftables/index.php/Simple_rule_management) - CLI version
- [Quick reference-nftables in 10 minutes](https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes)
^ permalink raw reply [flat|nested] 2+ messages in thread
* Re: [PROPOSAL] New wiki page: Simple rule management with JSON
2025-11-06 9:29 [PROPOSAL] New wiki page: Simple rule management with JSON Alexandre Knecht
@ 2025-11-06 23:15 ` Florian Westphal
0 siblings, 0 replies; 2+ messages in thread
From: Florian Westphal @ 2025-11-06 23:15 UTC (permalink / raw)
To: Alexandre Knecht; +Cc: netfilter
>
> I would like to propose a new wiki page that provides user-friendly
> documentation
> for nftables JSON rule management. This complements the technical reference in
> libnftables-json(5) with practical examples and beginner-friendly explanations.
Thanks for writing this!
I will make an account for you so you can add it yourself.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-11-06 23:15 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-06 9:29 [PROPOSAL] New wiki page: Simple rule management with JSON Alexandre Knecht
2025-11-06 23:15 ` Florian Westphal
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox