Block Outbound Traffic for a Specific Linux User with iptables (While Keeping Listening Ports Working)
In some environments you may want to prevent a particular local user from initiating outbound network connections, while still allowing that user to run services that listen on ports and respond to inbound connections. This can be achieved with iptables by blocking only NEW outbound connections from that user, while allowing ESTABLISHED/RELATED traffic to continue.
This article shows a clean, minimal ruleset to accomplish that 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.
- Root privileges (
sudo) conntracksupport (available on most modern Linux systems)- The user’s UID (recommended for accuracy)
Get the numeric UID for userA:
id -u userAIn the commands below, replace <UID_A> with the returned number.
This ensures return traffic for existing inbound connections (and other established flows) is not disrupted.
sudo iptables -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTMany local services rely on lo (127.0.0.1). Keeping it open avoids breaking internal IPC patterns over TCP.
sudo iptables -I OUTPUT 2 -o lo -j ACCEPTThis is the core rule.
sudo iptables -I OUTPUT 3 -m owner --uid-owner <UID_A> -m conntrack --ctstate NEW -j REJECTNotes:
REJECTfails fast and is easier to debug. If you prefer silent blocking, replaceREJECTwithDROP.- The
ownermatch works only for locally generated packets (OUTPUT), which is exactly what you want here.
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.
As a result:
- Outbound connection initiation is blocked.
- Inbound services can still function normally.
Check the active OUTPUT chain rules:
sudo iptables -S OUTPUT
sudo iptables -L OUTPUT -n -v --line-numbersTesting ideas:
-
As
userA, try:curl https://example.com(should fail)
-
From another host, connect to a service listening under
userA:- e.g.,
nc <server-ip> 8080(should connect and receive responses)
- e.g.,
If IPv6 is enabled, traffic may still escape via IPv6 unless you 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_A> -m conntrack --ctstate NEW -j REJECTThe owner module matches processes on the local host and is only meaningful for locally generated traffic. It does not apply to forwarded traffic (FORWARD), and it won’t help if you’re trying to police routing/NAT for other hosts.
If userA can run commands as another user (e.g., sudo to root), the traffic will be owned by a different UID and won’t match this rule. If that’s a concern, address it via sudo policy and system hardening.
Because the rule blocks only NEW, any already-established outbound connections may continue until they close naturally. If you need immediate cutoff, you’ll have to terminate the processes or kill existing connections (e.g., via ss/lsof workflows) in addition to adding rules.
Blocking outbound traffic for a specific Linux user while keeping listening ports functional is straightforward with iptables:
- Allow
ESTABLISHED,RELATED - Allow loopback
- Reject or drop user-owned
NEWoutbound connections in OUTPUT
This approach is minimally invasive and well-suited for service accounts where you want to limit egress without breaking inbound service behavior.