Block Outbound Traffic for a Specific Linux User with iptables (While Keeping Listening Ports Working)

Published: (January 19, 2026 at 05:18 AM EST)
3 min read
Source: Dev.to

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:8080 and 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).
  • conntrack support (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
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:

  • REJECT fails fast and is easier to debug. Use DROP for silent blocking.
  • The owner match 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

  1. -m owner applies only to OUTPUT
    The owner module 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.

  2. Privilege escalation can bypass the restriction
    If userA can run commands as another user (e.g., via sudo to 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.

  3. Existing connections may remain open
    Because the rule blocks only NEW, any already‑established outbound connections may continue until they close naturally. For an immediate cutoff, terminate the processes or kill existing connections (e.g., using ss/lsof).

Conclusion

Blocking outbound traffic for a specific Linux user while keeping listening ports functional is straightforward with iptables:

  1. Allow ESTABLISHED,RELATED traffic.
  2. Allow loopback.
  3. Reject (or drop) user‑owned NEW outbound 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.

Back to Blog

Related posts

Read more »

Top 5 CLI Coding Agents in 2026

Introduction The command line has always been home turf for developers who value speed, clarity, and control. By 2026, AI has settled comfortably into that spa...