Skip to content

fix: apply local security_rules to the kernel firewall on initial load#452

Merged
pigri merged 5 commits into
mainfrom
fix/local-security-rules-kernel-firewall
Jul 18, 2026
Merged

fix: apply local security_rules to the kernel firewall on initial load#452
pigri merged 5 commits into
mainfrom
fix/local-security-rules-kernel-firewall

Conversation

@pigri

@pigri pigri commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Problem

In standalone (no-API-key) mode, access rules loaded from a local
security_rules.yaml never reached the XDP/nftables firewall. Two causes:

  1. Parse failure on a hand-written file. Config.created_at /
    updated_at / last_modified were required (no serde default), so a
    minimal local rules file failed to deserialize (missing field created_at) and was silently dropped. These are API-envelope metadata,
    not fields a local author provides.

  2. Rules never programmed on initial load. The initial local-config
    load called apply_rules_from_global_with_state — which reads
    global_config()without first calling set_global_config. The
    block/allow IPs diffed against an empty config and were never written to
    the banned_ips map. Rules only took effect after a subsequent
    file-change reload, whose path already promotes to the global slot.

Net effect: a static block.ips entry silently did nothing on a fresh
start.

Fix

  • synapse-config: mark created_at / updated_at / last_modified
    #[serde(default)] so a local file with just access_rules + waf_rules
    parses.
  • synapse-worker: extract the initial local-load logic into
    apply_initial_local_config and call set_global_config before applying,
    so the loaded rules are programmed into the firewall on initial load —
    matching the reload path.

Tests

  • config_parses_without_metadata_timestamps
  • initial_local_config_is_promoted_to_global

cargo fmt / cargo clippy --deny warnings clean on both crates.

pigri added 2 commits July 18, 2026 12:47
In standalone (no-API-key) mode two issues stopped access rules loaded
from a local security_rules.yaml from ever reaching the XDP/nftables
firewall:

1. Config.created_at / updated_at / last_modified were required fields, so
   a hand-written local rules file failed to deserialize ("missing field
   created_at") and was silently ignored. These are API-envelope metadata,
   not something a local author supplies — mark them #[serde(default)] so a
   minimal local file (access_rules + waf_rules) parses.

2. The initial local-config load called apply_rules_from_global_with_state
   — which reads global_config() — without first calling set_global_config.
   The block/allow IPs therefore diffed against an empty config and were
   never programmed into the banned_ips map; the rules only took effect
   after a later file-change reload (whose path does promote). Extract the
   initial-load logic into apply_initial_local_config and promote the
   freshly-loaded config to the global slot before applying, matching the
   reload path.

Tests: config_parses_without_metadata_timestamps,
initial_local_config_is_promoted_to_global.
Add a regression test that a local security_rules.yaml is programmed into
the kernel XDP banned_ips map on the agent's INITIAL load (no file
re-touch), plus a workflow to run it on amd64 and arm64.

- scripts/local-rules-firewall-test.sh: runs the agent against an isolated
  `dummy` interface with a minimal rules file (no API metadata timestamps)
  and asserts the blocked IP appears in banned_ips via bpftool. No traffic
  or VM emulation needed; asserts the kernel map directly.
- .github/workflows/firewall-integration.yaml: build + run the test.

Guards both halves of the local-config fix (file parses without the API
metadata; rules reach the kernel firewall on initial load).
Comment thread .github/workflows/firewall-integration.yaml Fixed
pigri added 3 commits July 18, 2026 15:04
- drop arm64 job: arm runners have no binary-arm64 apt packages for the
  build deps (arm64 build coverage lives in build.yaml's container build);
  the tested behaviour is arch-independent config logic.
- install linux-tools-$(uname -r) so bpftool's kernel-specific binary is
  present (the generic package is only a wrapper); verify with 'bpftool version'.
The apt bpftool is a wrapper that execs a kernel-version-specific binary,
which isn't published for the GitHub runner's kernel. Download the static
upstream bpftool (sha256-verified) instead, and let the test honor a
BPFTOOL override. Validated: test passes with the static binary.
@pigri
pigri merged commit 1d339ca into main Jul 18, 2026
29 checks passed
@pigri
pigri deleted the fix/local-security-rules-kernel-firewall branch July 18, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants