EN
  • English
  • Deutsch
  • Polski
  • Italiano
  • Español

Security by Design

Warden treats security as a property of the daemon, not a feature list. The threat model names every in-scope threat, every control, and every deliberate omission. This page is the other half: how the posture is built, and the layers that page does not cover.

Defense in depth

Filtering is one layer. The named threats sit on the threat model.

A query is checked at the name, at the IPs in the answer, and at the host. The fourteen classes and the eight switches of the Operator Configuration Contract live there. Out of scope items belong to a different layer.

One extra layer that page does not list: [ip_blocklists]. Every A/AAAA in an upstream answer is checked against IP feeds. Fast-flux and hijacked CDN hosts are caught even when the domain is clean. Off until you enable it.

Devices can’t escape to a public resolver

A hardcoded 8.8.8.8 or a DoH/DoT endpoint makes the filter optional. Warden owns the name. The router owns the packet.

Warden ships no resolver list. You name the domains, or you subscribe a list. Encrypted DNS sent straight to an IP is out of scope at the DNS layer.

EscapeControlYou doLimit
DoH/DoT hostname you name[anti_bypass].extra_domainslist the domainsREFUSED before cache and filter. No allow rule can override. The client hears “unavailable”, not a forged address.
DoH/DoT hostname via catalogueservices/resolverssubscribe the listGoes through the filter engine. Your allows still win. The list arrives at base = "deny", so it blocks for every profile that has not overridden it to allow or ignore.
Hardcoded IP on port 53warden firewall-rulesapply the printed redirect on the routeriptables, ip6tables, and nftables. A redirect on a host that does not route the LAN catches nothing.
DoH/DoT straight to an IPsame command, ports 443 / 853apply the printed drop/reject rulesNever hits port 53. Warden prints. The firewall enforces.

External lists are sandboxed

A subscribed list can add block rules. That is all.

@@, $important, and regex are stripped at parse. They only take effect in your config.toml. The downloader is HTTPS-only and rejects private, CGNAT, and loopback IPs. The full split is the supply-chain and SSRF rows of the in-scope table.

Other DNS filters do not enforce this. A compromised list there can unblock a domain with @@. Here it cannot.

Safe by default

A config that would expose the network does not boot. An unsafe rule does not get written.

The open-resolver check and the refuse-unknown-clients posture are in the Operator Configuration Contract. These are the checks that page does not list:

MistakeWhenEffect
secrets.toml mode wider than 0600bootrefuse to start; error prints the chmod
Homoglyph in a rule you write (gооgle.com), reserved-IP local DNS, public-suffix wildcard (*.it, *.co.uk), device allow vs profile denywriteCLI reject; frozen English error

The write-time homoglyph check is for rules you type. Matching lookalikes in subscribed lists is out of scope.

Append-only audit log

What changed, who did it, when.

FactValue
Path/var/lib/purge-warden/audit/audit.log
Formatone JSON line per event, append-only
Actorkernel SO_PEERCRED UID for daemon events; UID of the warden process for CLI mutations
Lifecycle extraSHA-256 of the config tree before and after (boot, shutdown, reload, restore)
Per-rule CLI extraaction, scope, target_id, domain, rule_id, override_used — no config hashes
Mode0640, group purge-warden

Read it without root:

# last 20 events
warden audit tail -n 20

Daemon runs sandboxed, not as root

A compromised resolver is still not root, and still cannot see the rest of the host.

Compromise of the Warden host is out of scope. The unit still shrinks the blast radius. It is systemd/purge-warden.service. The installer wires it. The daemon binds port 53 with AmbientCapabilities=CAP_NET_BIND_SERVICE as user purge-warden.

SurfaceDirectives
Identityuser purge-warden, CAP_NET_BIND_SERVICE, never root
FilesystemProtectSystem=strict, PrivateTmp
PrivilegesNoNewPrivileges, SystemCallFilter
Memory / IPCMemoryDenyWriteExecute, LockPersonality, RestrictNamespaces, RemoveIPC

Verify it in the code

Controls unique to this page. The fourteen named threats are on the threat model.

IP-level blocking
src/filterip_filter.rs → IpFilter::check_response()
Anti-bypass
src/securityanti_bypass.rs → AntiBypass::is_bypass_domain()
src/cli/commandsfirewall_rules.rs
Fail-closed validation
src/config/schemavalidator.rs → validate()
src/configsecrets.rs → load_secrets()
Audit log
src/configaudit.rs → AuditLog::append()
src/ipcsocket_server.rs
Daemon sandboxing
systemdpurge-warden.service
↑↓ to navigate · Enter to open · Esc to close See all results