Webfilter - CyberMind-FR/secubox-deb GitHub Wiki
Webfilter — content filtering by category 🟡
EN | FR | 🟡 WALL | package secubox-webfilter
What it is for
Webfilter sorts the DNS requests of each device into categories (adult content, gambling, phishing and malware) and, when the administrator decides so, blocks them. It is a parental-control and dangerous-site filter that works at the DNS level, through Unbound, the only DNS engine of the box. By default it only observes: nothing is blocked until a profile says so.

Demonstration capture (fictitious data). Panel: /webfilter/ · API: /api/v1/webfilter/ · config: /etc/secubox/webfilter.toml. It ships in the secubox-lite profile (see Modules-by-Profile).
How it works
- Lists.
secubox-webfilter-syncdownloads the public lists of the catalogue (https only, size capped; the previous version is kept if a download fails or is truncated) and builds a compact index per source. The lists are downloaded at run time, never shipped in the package. Sync runs every 6 hours and on request. - Counting.
secubox-webfilter-feedfollows the Unbound journal, classifies each requested name (the name or one of its parents is listed) and counts per day, device, category and domain over 30 days. Requests from the box itself are excluded. - Display. The API and the panel show the result. The NAC device card and the Guardian panel read it in read-only mode (see NAC).
Categories: adulte, jeux (gambling), phishing (phishing and malware). Parking has no public list and is left to a later learning phase.
Profiles, devices and exceptions
- Profiles: each category is
observeorblock, plus per-domain allowances. Thedefautprofile applies to every device that is not assigned and cannot be deleted. Two templates,enfantsandadultes, are offered but never applied on their own. - Devices: the key is the MAC address (IPv4 and IPv6 together). A device gets a profile and per-category exceptions. Devices managed by ad-guard (TVs, streamers) keep their own filtering and cannot be assigned here (see DNS-Filtrage).
- Always-blocked domains (
bloque, 0.4.0): a profile and a device can each carry a list of domains that are refused whatever the mode of the categories, even when the category listing them is inobservefor that device. The device list adds to the profile list; a block wins over an allowance of the same name; an allowance on a parent domain does not unblock a forced sub-domain. Canonical form is required (lowercase, no scheme or path), 200 domains at most. The key is written only when it is used, so a configuration without forced domains stays identical to the one before.
Applying the configuration
Changes are saved by the API, then applied by a root controller started by a file, never by sudo:
POST /api/v1/webfilter/appliquerdrops a request file; the root unitsecubox-webfilter-apply(started by a.pathunit) runssecubox-webfilter-ctl apply.- The controller validates the configuration as hostile input, generates the Unbound drop-in, checks the zone budget, runs
unbound-checkconf, reloads Unbound and restores the previous file byte for byte on failure. An identical generated file triggers no reload. - The estimate (zones, memory, duration) is available before applying. A reload cuts DNS for roughly 7 to 10 seconds;
secubox-webfilter-apply.timerapplies pending changes at 04:00. Syncing the lists never reloads Unbound. - Every effective change (profile, category mode, device, application, refusal, failure) is written to the audit log.
Memory depends on the number of distinct effective configurations (devices with the same configuration share one Unbound view), not on the number of devices.
AppArmor fix (0.4.1)
The profile of the root controller was not allowed to delete the request file. The apply service failed, the .path unit hit its start limit and "Apply now" answered 409 ("already requested or running"). The profile now allows it.
The Aurora page
Since 0.3.0 the page follows the Aurora style: tiles per category, split between blocked and observed, most-filtered devices, all actions guarded. The blocking confirmations, ignored by the Hall browser when they were confirm() calls, are now in-page confirmations. The page announces its height to the host and supports ?embed=1 and ?demo=1.
Main routes
Guards: require_lecture for the state, require_jwt for the rest.
| Method | Path | Purpose |
|---|---|---|
| GET | /health |
module health (public) |
| GET | /etat |
categories, global mode, sources, requests classified over 7 days, no device name |
| GET | /stats, /categories/{id}/domaines |
counts per category and device, most seen domains |
| POST | /sync |
request a list synchronisation |
| GET/POST/DELETE | /profils, /profils/{nom} |
profiles |
| GET/POST/DELETE | /appareils, /appareils/{mac} |
devices assigned or recently seen |
| GET/POST | /appliquer |
pending changes, estimate, last result; apply request |
Limits and privacy
- A DoH resolver, a VPN or a direct IP address bypass a DNS filter; spoofing a MAC address makes a device inherit the profile of the spoofed one. A DNS filter is not an access control.
- The "blocked" count can be overestimated (it ignores per-domain allowances and excluded addresses); the reverse never happens.
- Per-device counts are browsing data: administrator only, kept 30 days, never exported from the box. The filtering of a device is visible in the panel; there is no hidden surveillance.
See also
DNS-Filtrage · NAC · Modules-by-Profile · Modules-by-Family · Admin-Panels-Gallery · Aurora-Design · Profils