423 lines
14 KiB
Markdown
423 lines
14 KiB
Markdown
# Firewall Validation Log — Red Bear OS Phase 4
|
|
|
|
**Created:** 2026-07-26
|
|
**Author:** Red Bear OS networking systematic fix plan, Phase 4
|
|
**Component:** `redbear-firewall-check` + `test-firewall-scenarios.sh`
|
|
**Requires:** `redbear-mini` ISO, `netfilter` scheme (base netstack), UID 0 (root)
|
|
|
|
## Overview
|
|
|
|
This document records the design, expected behavior, and validation procedure for
|
|
each of the six firewall validation scenarios. The scenarios exercise the
|
|
`netfilter:` scheme control plane in Red Bear OS — the packet-filter and NAT
|
|
subsystem in the `base` netstack daemon.
|
|
|
|
The validation harness has three parts:
|
|
|
|
| Part | Location | Purpose |
|
|
|------|----------|---------|
|
|
| `redbear-firewall-check` | `local/recipes/system/redbear-hwutils/source/src/bin/redbear-firewall-check.rs` | In-guest binary that runs all 6 scenarios against `/scheme/netfilter` |
|
|
| `test-firewall-scenarios.sh` | `local/scripts/test-firewall-scenarios.sh` | Host-side QEMU launcher for 3-VM topology |
|
|
| `FIREWALL-VALIDATION-LOG.md` | `local/docs/FIREWALL-VALIDATION-LOG.md` | This document |
|
|
|
|
## Netfilter Scheme Interface
|
|
|
|
The netfilter scheme (`/scheme/netfilter/`) exposes a filesystem-style API
|
|
authenticated by UID 0 (root). Operations:
|
|
|
|
| Path | Mode | Description |
|
|
|------|------|-------------|
|
|
| `rule/list` | Read | Text dump of all filter rules (mirrors `iptables -L -n -v`) |
|
|
| `rule/add` | Write | Append one rule (mirrors `iptables -A`) |
|
|
| `rule/del` | Write | Remove one rule by numeric id |
|
|
| `nat/list` | Read | Text dump of all NAT rules |
|
|
| `nat/add` | Write | Append one NAT rule |
|
|
| `nat/del` | Write | Remove one NAT rule by numeric id |
|
|
| `policy/<chain>` | Read/Write | Default policy for INPUT, OUTPUT, FORWARD, PREROUTING, POSTROUTING |
|
|
| `reset` | Write | Clear all rules, restore default ACCEPT policies, reset conntrack |
|
|
| `stats` | Read | Per-chain packet/byte counters, rule counts, NAT counts, conntrack summary |
|
|
| `conntrack/list` | Read | Per-connection entry dump with 5-tuple + packet/byte counters |
|
|
| `conntrack/stats` | Read | Per-protocol/state breakdown (TCP SYN/EST/FIN/TW/Close, UDP, ICMP) + `over_limit` + `max_entries` |
|
|
| `counters/reset` | Write | Zero per-chain and per-rule counters (preserves rules) |
|
|
| `log` | Read | Last 20 LOG verdict entries |
|
|
|
|
### Rule Grammar
|
|
|
|
```
|
|
ACTION CHAIN [-i IFACE] [-o IFACE] [-p PROTO] [-s ADDR[/LEN]] [-d ADDR[/LEN]]
|
|
[--sport PORT] [--dport PORT] [state STATE|--ctstate STATE[,STATE...]]
|
|
```
|
|
|
|
**Actions:** ACCEPT, DROP, LOG, REJECT
|
|
**Chains:** input, output, forward, prerouting, postrouting
|
|
**Protocols:** tcp, udp, icmp, icmp6, or number
|
|
**States:** new, established, related, invalid (comma-separated for --ctstate)
|
|
|
|
Examples:
|
|
- `ACCEPT input --ctstate ESTABLISHED,RELATED`
|
|
- `DROP input -s 10.0.0.0/8`
|
|
- `ACCEPT input -p tcp --dport 22`
|
|
- `ACCEPT output -d 192.168.1.0/24 --sport 1024`
|
|
|
|
### NAT Rule Grammar
|
|
|
|
```
|
|
SNAT|DNAT hook to ADDR [port N] [match-src ADDR] [match-dst ADDR]
|
|
```
|
|
|
|
Examples:
|
|
- `SNAT postrouting to 10.0.2.15` — masquerade outgoing traffic
|
|
- `DNAT prerouting to 10.0.0.2 port 8080` — port forward 80→8080
|
|
- `SNAT postrouting to 10.0.2.15 match-src 10.0.0.1` — source-specific SNAT
|
|
|
|
---
|
|
|
|
## Scenario A: AcceptEstablished
|
|
|
|
### Purpose
|
|
Verify that the default-deny INPUT + ACCEPT ESTABLISHED/RELATED pattern works.
|
|
This is the most common iptables pattern: drop all incoming new connections,
|
|
but allow responses to locally-initiated outbound traffic.
|
|
|
|
### Prereq netcfg state
|
|
- Firewall reset to defaults (all policies ACCEPT, no rules).
|
|
- No network interfaces need to be configured at this level — the test
|
|
validates the rule parser and scheme, not actual packet flow.
|
|
|
|
### Exact ufw/netfilter commands
|
|
```bash
|
|
# Via scheme writes (equivalent to these netfilter scheme operations):
|
|
echo "DROP" > /scheme/netfilter/policy/input
|
|
echo "ACCEPT input --ctstate ESTABLISHED,RELATED" > /scheme/netfilter/rule/add
|
|
echo "1" > /scheme/netfilter/reset
|
|
```
|
|
|
|
### What the test does
|
|
1. Resets firewall.
|
|
2. Sets INPUT policy to DROP.
|
|
3. Verifies INPUT policy reads back as "DROP".
|
|
4. Verifies OUTPUT policy remains "ACCEPT" (default).
|
|
5. Adds `ACCEPT input --ctstate ESTABLISHED,RELATED` rule.
|
|
6. Verifies the rule appears in `rule/list` with a valid numeric id.
|
|
7. Verifies `stats` endpoint returns chain counters for both input and output.
|
|
8. Resets firewall.
|
|
|
|
### Expected output
|
|
```
|
|
Scenario A (AcceptEstablished) ... pass
|
|
SCENARIO_A=pass
|
|
```
|
|
|
|
### Cleanup
|
|
- Firewall reset to defaults (all policies ACCEPT, empty rules).
|
|
|
|
---
|
|
|
|
## Scenario B: SSHAllow
|
|
|
|
### Purpose
|
|
Verify port-specific ACCEPT rules coexist correctly with the established-connections
|
|
rule. This simulates opening a single service port (SSH on 22) while keeping
|
|
the rest of INPUT denied.
|
|
|
|
### Prereq netcfg state
|
|
- Firewall reset to defaults.
|
|
|
|
### Exact ufw/netfilter commands
|
|
```bash
|
|
echo "DROP" > /scheme/netfilter/policy/input
|
|
echo "ACCEPT input -p tcp --dport 22" > /scheme/netfilter/rule/add
|
|
echo "ACCEPT input --ctstate ESTABLISHED,RELATED" > /scheme/netfilter/rule/add
|
|
echo "1" > /scheme/netfilter/reset
|
|
```
|
|
|
|
### What the test does
|
|
1. Resets firewall.
|
|
2. Sets INPUT policy to DROP.
|
|
3. Adds ACCEPT rule for tcp/22.
|
|
4. Adds ACCEPT rule for ESTABLISHED,RELATED.
|
|
5. Verifies both rules appear in `rule/list`.
|
|
6. Verifies the SSH rule contains `dport=22` but NOT `dport=80` (no rule leakage).
|
|
7. Resets firewall.
|
|
|
|
### Expected output
|
|
```
|
|
Scenario B (SSHAllow) ... pass
|
|
SCENARIO_B=pass
|
|
```
|
|
|
|
### Cleanup
|
|
- Firewall reset to defaults.
|
|
|
|
---
|
|
|
|
## Scenario C: SNATMASQ
|
|
|
|
### Purpose
|
|
Verify Source NAT (masquerade) rule configuration. This simulates a router
|
|
with an internal subnet 10.0.0.0/24 whose outbound traffic is rewritten
|
|
to use the router's external IP.
|
|
|
|
### Prereq netcfg state
|
|
- Firewall reset to defaults.
|
|
- No actual routing needed — the test validates the NAT table control plane.
|
|
|
|
### Exact ufw/netfilter commands
|
|
```bash
|
|
echo "SNAT postrouting to 10.0.2.15 match-src 10.0.0.1" > /scheme/netfilter/nat/add
|
|
echo "1" > /scheme/netfilter/reset
|
|
```
|
|
|
|
### What the test does
|
|
1. Resets firewall.
|
|
2. Adds SNAT rule: `SNAT postrouting to 10.0.2.15 match-src 10.0.0.1`.
|
|
3. Reads `nat/list` and verifies the rule appears with `Snat` type and `10.0.2.15` target.
|
|
4. Verifies `nat/list` header (`Nat table:`) is present.
|
|
5. Reads `stats` and verifies it includes `snat=` and `dnat=` counters.
|
|
6. Resets firewall.
|
|
|
|
### Expected output
|
|
```
|
|
Scenario C (SNATMASQ) ... pass
|
|
SCENARIO_C=pass
|
|
```
|
|
|
|
### Cleanup
|
|
- Firewall reset to defaults.
|
|
|
|
---
|
|
|
|
## Scenario D: DNATPortFwd
|
|
|
|
### Purpose
|
|
Verify Destination NAT (port forwarding) rule configuration. This simulates
|
|
redirecting incoming traffic on the router's public IP port 80 to an internal
|
|
server at 10.0.0.2:8080.
|
|
|
|
### Prereq netcfg state
|
|
- Firewall reset to defaults.
|
|
|
|
### Exact ufw/netfilter commands
|
|
```bash
|
|
echo "DNAT prerouting to 10.0.0.2 port 8080" > /scheme/netfilter/nat/add
|
|
echo "SNAT postrouting to 10.0.2.15" > /scheme/netfilter/nat/add
|
|
echo "1" > /scheme/netfilter/reset
|
|
```
|
|
|
|
### What the test does
|
|
1. Resets firewall.
|
|
2. Adds DNAT rule: `DNAT prerouting to 10.0.0.2 port 8080`.
|
|
3. Verifies the rule appears in `nat/list` with `Dnat`, `10.0.0.2`, and `8080`.
|
|
4. Verifies port info (`8080`) is present in the formatted output.
|
|
5. Adds a second NAT rule (SNAT) to verify coexistence.
|
|
6. Verifies both SNAT and DNAT rules appear in the combined NAT table.
|
|
7. Resets firewall.
|
|
|
|
### Expected output
|
|
```
|
|
Scenario D (DNATPortFwd) ... pass
|
|
SCENARIO_D=pass
|
|
```
|
|
|
|
### Cleanup
|
|
- Firewall reset to defaults.
|
|
|
|
---
|
|
|
|
## Scenario E: ConntrackLifecycle
|
|
|
|
### Purpose
|
|
Verify that the connection tracking (conntrack) module is initialized and
|
|
reporting statistics. The conntrack table tracks TCP state transitions
|
|
(SYN_SENT → SYN_RECV → ESTABLISHED → FIN_WAIT → TIME_WAIT), UDP streams,
|
|
and ICMP echo exchanges.
|
|
|
|
### Prereq netcfg state
|
|
- Firewall reset to defaults.
|
|
- Conntrack module auto-initialized (FilterTable::new creates ConntrackTable).
|
|
|
|
### Exact ufw/netfilter commands
|
|
```bash
|
|
cat /scheme/netfilter/conntrack/stats
|
|
cat /scheme/netfilter/conntrack/list
|
|
```
|
|
|
|
### What the test does
|
|
1. Resets firewall.
|
|
2. Reads `conntrack/stats` and verifies all required fields:
|
|
- `tcp_entries:` (with state breakdown in parentheses)
|
|
- `udp_entries:`
|
|
- `icmp_entries:`
|
|
- `over_limit:` (parsed as u64)
|
|
- `total_entries:` (parsed as u64)
|
|
- `max_entries:` (must be > 0)
|
|
3. Reads `conntrack/list` and verifies the header (`conntrack entries:`) is present.
|
|
4. Verifies the `total_entries` value is a valid u64 number.
|
|
5. Resets firewall.
|
|
|
|
### Expected output
|
|
```
|
|
Scenario E (ConntrackLifecycle) ... pass
|
|
SCENARIO_E=pass
|
|
```
|
|
|
|
### Cleanup
|
|
- Firewall reset to defaults (also resets conntrack table).
|
|
|
|
---
|
|
|
|
## Scenario F: SYNFlood
|
|
|
|
### Purpose
|
|
Verify that the SYN rate-limiting infrastructure is wired into the conntrack
|
|
module. The conntrack table has a per-source-IP SYN counter (limit: 100 SYN/sec,
|
|
window: 1 second) that increments the `over_limit` counter when a source exceeds
|
|
the threshold.
|
|
|
|
### Prereq netcfg state
|
|
- Firewall reset to defaults.
|
|
|
|
### Exact ufw/netfilter commands
|
|
```bash
|
|
cat /scheme/netfilter/conntrack/stats
|
|
cat /scheme/netfilter/stats
|
|
```
|
|
|
|
### What the test does
|
|
1. Resets firewall.
|
|
2. Reads `conntrack/stats` and verifies `over_limit` field exists and parses as u64.
|
|
3. Logs the initial `over_limit` value (expected 0; non-zero accepted as traffic residue).
|
|
4. Reads `max_entries` and verifies it is > 0 (table is enabled, not size-0).
|
|
5. Verifies `icmp_errors` field exists (independent rate limit structure from SYN limit).
|
|
6. Reads `stats` and verifies `rules:` field exists.
|
|
7. Resets firewall.
|
|
|
|
### Expected output
|
|
```
|
|
Scenario F (SYNFlood) ... pass
|
|
SCENARIO_F=pass
|
|
```
|
|
|
|
### Cleanup
|
|
- Firewall reset to defaults.
|
|
|
|
---
|
|
|
|
## 3-VM QEMU Topology
|
|
|
|
The `test-firewall-scenarios.sh` script creates three VMs connected by
|
|
AF_UNIX socket pairs, simulating a routed network:
|
|
|
|
```
|
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
|
│ VM-A │ │ Router-VM │ │ VM-B │
|
|
│ (internal) │◄───►│ 2 NICs │◄───►│ (external) │
|
|
│ 10.0.0.2/24 │ │ eth0: 10.0.0.1 │ │ 10.0.2.200/24│
|
|
└─────────────┘ │ eth1: 10.0.2.100│ └─────────────┘
|
|
└─────────────┘
|
|
```
|
|
|
|
### Network configuration
|
|
|
|
| VM | NIC | MAC | Subnet | Role |
|
|
|----|-----|-----|--------|------|
|
|
| Router-VM | eth0 | 52:54:00:12:34:01 | 10.0.0.0/24 | Internal gateway |
|
|
| Router-VM | eth1 | 52:54:00:12:34:02 | 10.0.2.0/24 | External NAT interface |
|
|
| VM-A | eth0 | 52:54:00:12:34:0A | 10.0.0.0/24 | Internal client |
|
|
| VM-B | eth0 | 52:54:00:12:34:0B | 10.0.2.0/24 | External peer |
|
|
|
|
### Packet capture
|
|
|
|
Each NIC has a QEMU `filter-dump` object that writes a pcap file:
|
|
- `artifacts/firewall-<date>/router-eth0.pcap` — Internal subnet traffic
|
|
- `artifacts/firewall-<date>/router-eth1.pcap` — External subnet traffic
|
|
- `artifacts/firewall-<date>/vm-a-net0.pcap` — Internal peer traffic
|
|
- `artifacts/firewall-<date>/vm-b-net0.pcap` — External peer traffic
|
|
|
|
### Automated check mode
|
|
|
|
```bash
|
|
# Build the ISO first:
|
|
./local/scripts/build-redbear.sh redbear-mini
|
|
|
|
# Run the automated firewall check:
|
|
./local/scripts/test-firewall-scenarios.sh --check redbear-mini
|
|
```
|
|
|
|
This boots the Router-VM, logs in via expect, runs `redbear-firewall-check`,
|
|
captures all 6 scenario results, and exits with code 0 only if all pass.
|
|
|
|
### Interactive mode
|
|
|
|
```bash
|
|
./local/scripts/test-firewall-scenarios.sh redbear-mini
|
|
```
|
|
|
|
All three VMs boot and wait. Log into Router-VM manually and run
|
|
`redbear-firewall-check`. Press Ctrl+C to stop all VMs.
|
|
|
|
---
|
|
|
|
## Recipe Integration
|
|
|
|
The `redbear-firewall-check` binary is part of the `redbear-hwutils` crate.
|
|
It is built alongside other validation binaries (lspci, redbear-phase5-*, etc.)
|
|
via the `[[bin]]` entry in `Cargo.toml` and mapped to a `/usr/bin` path in
|
|
`recipe.toml` under `[package.files]`.
|
|
|
|
### Files modified
|
|
|
|
| File | Change |
|
|
|------|--------|
|
|
| `local/recipes/system/redbear-hwutils/source/Cargo.toml` | Added `[[bin]]` entry |
|
|
| `local/recipes/system/redbear-hwutils/recipe.toml` | Added `/usr/bin/redbear-firewall-check` line |
|
|
| `local/recipes/system/redbear-hwutils/source/src/bin/redbear-firewall-check.rs` | New file — 6 scenarios |
|
|
| `local/scripts/test-firewall-scenarios.sh` | New file — 3-VM QEMU launcher |
|
|
| `local/docs/FIREWALL-VALIDATION-LOG.md` | New file — this document |
|
|
|
|
---
|
|
|
|
## Limitations
|
|
|
|
1. **Control-plane validation only.** The `redbear-firewall-check` binary
|
|
validates the netfilter scheme control plane (rule add/del, NAT add/del,
|
|
policy read/write, stats read, conntrack stats read). It does NOT generate
|
|
actual network traffic through the netstack's filter evaluation path.
|
|
End-to-end traffic validation requires the 3-VM QEMU topology with
|
|
real packet flow and pcap capture.
|
|
|
|
2. **SYN flood detection.** The test verifies the `over_limit` counter and
|
|
rate-limit infrastructure exist. Actual SYN flood trigger requires
|
|
>100 SYN packets in <1 second from a single source through the netstack —
|
|
this is validated by the pcap captures in the 3-VM topology, not by
|
|
the in-guest binary alone.
|
|
|
|
3. **NAT packet rewriting.** The test verifies NAT rules are parsed and stored.
|
|
Actual IP/port rewriting by `rewrite_src_ipv4` / `rewrite_dst_ipv4` /
|
|
`rewrite_port_ipv4` requires real packets routed through the netstack,
|
|
which is validated by the 3-VM QEMU topology.
|
|
|
|
4. **No per-scenario network traffic generation.** Each scenario resets
|
|
the firewall, configures rules, and reads back the state. Generating
|
|
and capturing meaningful test traffic across the 3-VM topology is
|
|
a separate phase (manual or scripted from inside each guest VM).
|
|
|
|
---
|
|
|
|
## Future Work
|
|
|
|
- **Phase 4.1:** Add packet generation from inside each guest VM
|
|
(e.g., `nc` / `socat` / `curl` with the Red Bear netstack socket API)
|
|
to generate actual test traffic through the filter.
|
|
|
|
- **Phase 4.2:** Add per-scenario pcap verification — the `filter-dump`
|
|
captures can be analyzed by host-side scripts to verify that packets
|
|
were/were not transmitted according to filter rules.
|
|
|
|
- **Phase 4.3:** Add `redbear-firewall-check --traffic` mode that spawns
|
|
real TCP/UDP/ICMP traffic inside the guest and measures conntrack
|
|
state transitions and rule match counts.
|
|
|
|
- **Phase 4.4:** Integrate with the existing network test framework
|
|
(`test-phase5-network-qemu.sh`) for combined network+firewall validation.
|