* Add per-client firewall filtering
Implement server-side firewall rules to restrict client network access,
allowing administrators to enforce security policies that cannot be
bypassed by clients modifying their local configuration.
This feature addresses the limitation where "Allowed IPs" only controls
client-side routing but doesn't prevent clients from accessing networks
they shouldn't reach. The firewall rules are enforced on the server
using iptables/ip6tables and provide true access control.
Features:
- Opt-in via "Enable Per-Client Firewall" toggle in admin interface
- Per-client "Firewall Allowed IPs" field for granular control
- Support for IPs, CIDRs, and port-based filtering
- Protocol specification: TCP, UDP, or both (default)
- IPv4 and IPv6 dual-stack support
- Falls back to client's allowedIps when firewallIps is empty
- Clean separation of routing (allowedIps) from security (firewallIps)
Supported formats:
- 10.10.0.3 (single IP)
- 10.10.0.0/24 (CIDR range)
- 192.168.1.5:443 (IP with port, both TCP+UDP)
- 192.168.1.5:443/tcp (IP with specific protocol)
- [2001:db8::1]:443 (IPv6 with port)
Implementation:
- New database columns: firewall_enabled (interfaces), firewall_ips (clients)
- Migration 0003_add_firewall_filtering for schema updates
- firewall.ts utility for iptables chain management (WG_CLIENTS chain)
- Integration into WireGuard.ts for automatic rule application
- UI components with conditional rendering based on firewall toggle
Technical details:
- Uses custom WG_CLIENTS iptables chain for isolation
- Rebuild strategy: flush and recreate all rules on config save
- Mutex protection via rebuildInProgress/rebuildQueued flags
- Graceful cleanup when firewall is disabled
- No new dependencies (uses existing is-ip, is-cidr packages)
* added Comprehensive documentation in README and docs/ for firewall
filtering
* validate firewall IPs
* check for iptables before enabling the firewall and inform the user if
it is missing
* updated firewall docs
* fix imports
* remove extra import
* Document all allowed IP/cidr/port/proto combinations that are allowed
and check on save
* add note on firewall being experimental and how to opt a single client
out of the firewall.
* cleanup more imports
* add tests
* Fix firewall IPv6 validation and test expectations
Updated validation to correctly handle plain and bracketed IPv6 addresses, and fixed test to expect string from schema instead of object.
* added comments to firewall rules and updated tests
* fix auto-import
* fix typescript errors
* recreate sql migrations and rebase
* improve tests, typechecking, documentation
* fix formatting, fix types
* improve type
* added note for including host's IP in client firewall
* updated language to include cidr and protocol options
* another language update
* refer to docs for firewall allowed IPs
---------
Co-authored-by: Bernd Storath <[email protected]>
* Extend docs for routed setup with nftables
When using nftables in a routed setup different up and down hooks need to be used.
To limit interaction with docker managed chains a custom WG_EASY chain is added as a jump target.
Since nft only supports deletion via handles awk is needed to get the handle of the jump rule for deletion
* Remove link to podman-nft
* Fix formatting according to prettier rules
* Add additional whitespace
- Changed setup/2.post.ts to use WG_INTERFACE_OVERRIDE_ENV.PORT instead of WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT
- Changed sqlite.ts initialSetup to use WG_INTERFACE_OVERRIDE_ENV.PORT for consistency
- Updated unattended-setup.md documentation:
- Changed INIT_PORT description to clarify it sets both interface port and endpoint port
- Updated warning text to reference WG_PORT (not WG_CLIENT_PORT) as the override fallback
- This matches the original INIT_PORT behavior where updateHostPort() sets both ports to the same value
Co-authored-by: kaaax0815 <[email protected]>
- Fixed setup/2.post.ts to use WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT instead of WG_INTERFACE_OVERRIDE_ENV.PORT
- Fixed sqlite.ts initialSetup to use WG_CLIENT_OVERRIDE_ENV.CLIENT_PORT for consistency
- Corrected unattended-setup.md documentation:
- Changed INIT_HOST and INIT_PORT from group "1*" to group "2"
- Clarified INIT_PORT description as "Port clients will connect to (endpoint port)"
- Updated group numbers: DNS is group 3, CIDR is group 4, Allowed IPs is group 5
- Fixed warning text: setup requires groups 1 and 2, and use WG_CLIENT_PORT (not WG_PORT)
- Ensures consistency between code behavior and documentation
Co-authored-by: kaaax0815 <[email protected]>
- Modified initialSetup to use WG_* override vars as fallback for INIT_* vars
- Split group 1: USERNAME and PASSWORD remain in group 1
- Moved HOST and PORT to group 2 (can use WG_HOST and WG_CLIENT_PORT)
- DNS moved to group 3 (can use WG_DEFAULT_DNS)
- CIDR moved to group 4 (can use WG_IPV4_CIDR and WG_IPV6_CIDR)
- Allowed IPs moved to group 5 (can use WG_DEFAULT_ALLOWED_IPS)
- Updated documentation to explain override fallback behavior
- Setup can now be skipped with INIT_USERNAME, INIT_PASSWORD, and override vars
Co-authored-by: kaaax0815 <[email protected]>
- Corrected override behavior description in optional-config.md
- Web UI displays database values with warning indicators, not overridden values
- Clarified that overrides take precedence at runtime, not in the UI display
Co-authored-by: kaaax0815 <[email protected]>
Backend changes:
- Added WG_METRICS_PASSWORD environment variable override
- Updated applyGeneralOverrides() to include metrics password
- Updated /api/admin/overrides endpoint to include metrics password
Frontend changes:
- Added override indicators (warning icons with tooltips) to all form fields
- Updated TextField, NumberField, NullTextField, SwitchField, HostField, ArrayField components
- Added overridden prop support to all form components
- Fetched /api/admin/overrides in all admin pages (interface, general, config, hooks)
- Warning icon displays when field is overridden by environment variable
- ArrayField shows banner when overridden
- Updated documentation with WG_METRICS_PASSWORD
Co-authored-by: kaaax0815 <[email protected]>
- Added WG_PRE_UP, WG_POST_UP, WG_PRE_DOWN, WG_POST_DOWN environment variables
- Created applyHooksOverrides() helper function
- Updated WireGuard service to apply hooks overrides when generating config
- Updated /api/admin/overrides endpoint to include hooks override information
- Updated documentation with hooks environment variables
Co-authored-by: kaaax0815 <[email protected]>
- Remove WG_ENABLED environment variable (interface cannot be disabled)
- Allow all admin panel updates to be saved to database
- Environment variable overrides take precedence at runtime only
- Users can now update values in admin panel even when overridden
- Updated documentation to clarify override behavior
Co-authored-by: kaaax0815 <[email protected]>
* docs: Add AdGuard Home tutorial
Signed-off-by: Edgar R.N <[email protected]>
* docs: Update AdGuard Home tutorial to use multi-network architecure
Signed-off-by: Edgar R.N <[email protected]>
* docs: Refine AdGuard Home tutorial based on feedback
Signed-off-by: Edgar R.N <[email protected]>
* docs: Temporary fix multi-network iptables
Signed-off-by: Edgar R.N <[email protected]>
* docs: AdGuard Home tutorial compatible with wg-easy v15
Signed-off-by: Edgar R.N <[email protected]>
---------
Signed-off-by: Edgar R.N <[email protected]>
* Add INIT_ALLOWED_IPS env var
Implement INIT_ALLOWED_IPS env var like the INIT_DNS to preset the global Allowed IPs field.
* Docs: Add INIT_ALLOWED_IPS var to unattended setup table
* Make UserConfigService.update param partial
Update UserConfigService.update() to accept any subset of the updatable fields.
Remove the unnecessary userConfig object from DBService.initialSetup()
* formatting fix
* format on linux
On windows prettier get confused by global conf... common windows things
* initial support for initial setup
* improve setup
* improve mobile view
* move base admin route
* admin panel mobile view
* set initial host and port
* add docs
* properly setup everything, use for dev env
* change userconfig and interface port on setup, note users afterwards