Block Outbound Traffic for a Specific Linux User with iptables (While Keeping Listening Ports Working)
Source: Dev.to
Goal
For userA:
- Block new outbound connections (e.g.,
curl, package downloads, external API calls). - Keep port listening and inbound service responses working (e.g., a daemon binding to
0.0.0.0:8080and responding to remote clients).
The key is to add a rule in the OUTPUT chain that matches the user and drops/rejects only traffic in conntrack state NEW.
Prerequisites
- Root privileges (
sudo). conntracksupport (available on most modern Linux systems).- The user’s UID (recommended for accuracy).
Identify the UID of the User
id -u userA
Replace the blank space in the commands below with the returned number.
Apply iptables Rules (IPv4)
Allow established traffic first (important)
sudo iptables -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Allow loopback traffic (recommended)
sudo iptables -I OUTPUT 2 -o lo -j ACCEPT
Block NEW outbound connections for the user (core rule)
sudo iptables -I OUTPUT 3 -m owner --uid-owner <UID> -m conntrack --ctstate NEW -j REJECT
Notes:
REJECTfails fast and is easier to debug. UseDROPfor silent blocking.- The
ownermatch works only for locally generated packets (OUTPUT), which is exactly what you want here.
Why This Keeps Listening Ports Working
A user process can still:
- Bind and listen on a port (listening itself is not a network transmission).
- Accept inbound connections initiated by remote clients.
When a remote host connects to your service, the responses from your server are part of an already established connection. Those response packets are classified as ESTABLISHED, so they are permitted by the first rule.
Result
- Outbound connection initiation is blocked.
- Inbound services continue to function normally.
Verify the Rules
sudo iptables -S OUTPUT
sudo iptables -L OUTPUT -n -v --line-numbers
Testing ideas
As userA, try:
curl https://example.com # should fail
From another host, connect to a service listening under userA:
nc 8080 # should connect and receive responses
Don’t Forget IPv6 (Common Bypass)
If IPv6 is enabled, apply equivalent ip6tables rules:
sudo ip6tables -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -I OUTPUT 2 -o lo -j ACCEPT
sudo ip6tables -I OUTPUT 3 -m owner --uid-owner <UID> -m conntrack --ctstate NEW -j REJECT
Operational Considerations and Caveats
-
-m ownerapplies only to OUTPUT
Theownermodule matches processes on the local host and is only meaningful for locally generated traffic. It does not apply to forwarded traffic (FORWARD) or to other hosts. -
Privilege escalation can bypass the restriction
IfuserAcan run commands as another user (e.g., viasudoto root), the traffic will be owned by a different UID and won’t match this rule. Mitigate by tightening sudo policies and hardening the system. -
Existing connections may remain open
Because the rule blocks onlyNEW, any already‑established outbound connections may continue until they close naturally. For an immediate cutoff, terminate the processes or kill existing connections (e.g., usingss/lsof).
Conclusion
Blocking outbound traffic for a specific Linux user while keeping listening ports functional is straightforward with iptables:
- Allow
ESTABLISHED,RELATEDtraffic. - Allow loopback.
- Reject (or drop) user‑owned
NEWoutbound connections in the OUTPUT chain.
This minimally invasive approach is well‑suited for service accounts where you want to limit egress without breaking inbound service behavior.