4.8 KiB
User Firewall Module
This NixOS module provides network access restrictions for non-privileged users, ensuring they can only access local services and VPN interfaces while blocking direct internet access.
Overview
The user-firewall module implements firewall rules that:
- Block all outbound network traffic for normal (non-system) users by default
- Allow specific users to bypass restrictions (exemptUsers)
- Permit traffic on specific interfaces (like VPNs and localhost)
- Support both iptables and nftables backends
- Handle both IPv4 and IPv6 traffic
Installation
Add the module to your NixOS configuration:
{
imports = [
self.inputs.clan-core.nixosModules.user-firewall
];
}
The module is automatically enabled once imported. It will immediately start restricting network access for all normal users except those listed in exemptUsers.
Configuration
Basic Usage
{
networking.user-firewall = {
exemptUsers = [ "alice" ]; # Users who can access the internet
};
}
Full Configuration Example
{
networking.user-firewall = {
# Users who are exempt from network restrictions
exemptUsers = [
"alice"
"admin"
];
# Network interfaces that all users can use
# Default includes common VPN interfaces
allowedInterfaces = [
"lo" # localhost (required for local services)
"tun*" # OpenVPN, OpenConnect
"wg*" # WireGuard (wg0, wg-home, etc.)
"tailscale*" # Tailscale
# Add custom interfaces as needed
];
};
}
How It Works
-
User Classification: The module automatically identifies all normal users (non-system users) and applies restrictions to those not in the
exemptUserslist. -
Firewall Rules:
- For iptables: Creates a custom chain
user-firewall-outputin the OUTPUT table - For nftables: Creates a table
inet user-firewallwith an output chain - Rules check outgoing packets and reject those from restricted users
- For iptables: Creates a custom chain
-
Interface Patterns: Supports wildcards in interface names:
*matches any characters (e.g.,wg*matcheswg0,wg-home)
Default Allowed Interfaces
The module comes with sensible defaults for common VPN and overlay network interfaces:
lo- Loopback (localhost access)tun*- OpenVPN, OpenConnecttap*- OpenVPN (bridged mode)wg*- WireGuardtailscale*- Tailscalezt*- ZeroTierhyprspace- Hyprpspacevpn*- Generic VPN interfacesnebula*- Nebula mesh networktinc*- Tinc VPNedge*- n2nham0- Hamachieasytier- EasyTiermycelium- Mycelium
Use Cases
1. Public Kiosk Systems
Restrict users to only access local services:
{
networking.user-firewall = {
allowedInterfaces = [ "lo" ]; # Only localhost
exemptUsers = [ ]; # No exempt users
};
}
2. Corporate Workstations
Force all traffic through corporate VPN:
{
networking.user-firewall = {
allowedInterfaces = [ "lo" "wg-corp" ];
exemptUsers = [ "sysadmin" ];
};
}
Testing
The module includes comprehensive tests for both iptables and nftables backends:
# Run iptables backend test
nix build .#checks.x86_64-linux.user-firewall-iptables
# Run nftables backend test
nix build .#checks.x86_64-linux.user-firewall-nftables
Troubleshooting
Check Active Rules
The output includes package counters for each firewall rule, that can help to debug connectivity issues.
For iptables:
sudo iptables -L user-firewall-output -n -v
sudo ip6tables -L user-firewall-output -n -v
For nftables:
sudo nft list table inet user-firewall
# Watch counters in real-time
sudo watch -n1 'nft list table inet user-firewall'
Check which rule your VPN traffic is hitting. If packets are being rejected, verify:
- Your VPN interface name matches the patterns in
allowedInterfaces - Your user is listed in
exemptUsersif needed
To see your current network interfaces:
ip link show | grep -E '^[0-9]+:'
Common Issues
-
Service Connection Failures: If local services fail to connect, ensure
lois inallowedInterfaces. -
VPN Not Working: Check that your VPN interface name matches the patterns in
allowedInterfaces. You can find your interface name withip link show. -
User Still Has Access: Verify the user is a normal user (not a system user) and not in
exemptUsers.
Security Considerations
- This module provides defense in depth but should not be the only security measure
- System users (like
nginx,systemd-*) are not restricted - Root user always has full network access
- Restrictions apply at the packet filter level, not application level
Limitations
- Requires
networking.firewall.enable = true - Cannot restrict system users or root
- Interface patterns are evaluated at rule creation time, not dynamically