Securing the Internal Network: Beyond the Perimeter
Most security setups resemble a medieval castle: thick outer walls but zero internal gates. Once a packet clears the perimeter firewall, the internal network is often a free-for-all. I spent six months managing a high-traffic, multi-tier environment before realizing that this “castle and moat” strategy is a liability. A single compromised Nginx instance shouldn’t grant an attacker a free pass to your entire database cluster.
I moved our production stack to a host-level security model using nftables. The results were immediate and stable. By implementing microsegmentation, we shifted security logic directly to the workload. This follows the core Zero Trust mandate: never trust, always verify, and strictly limit communication to the absolute minimum required for the service to function.
Comparing Strategies: Legacy Firewalls vs. Microsegmentation
Understanding the shift in philosophy is vital before you touch the command line. Traditional approaches focus on the edge, while microsegmentation focuses on the individual service.
1. Perimeter-Based Security (The Legacy Way)
Legacy models rely on a single entry point. Internal services like Web, App, and Database usually sit in the same VLAN. There are rarely restrictions between them. If an attacker hits a Web server with a remote code execution (RCE) exploit, they have a straight shot at your sensitive data tier.
2. Network Microsegmentation (The Zero Trust Way)
This method treats every service as an isolated island. Even if two services share the same physical subnet, they cannot communicate unless a specific rule allows it. nftables provides a high-performance framework to filter these packets at the kernel level. It offers a much cleaner syntax and better resource efficiency than the older iptables.
Why nftables Wins in Production
Switching to nftables for internal traffic control offers several technical wins, though it does require your team to learn a new syntax.
Pros
- Atomic Operations: You can replace entire rulesets in one go. This eliminates the race conditions found in
iptables, where rules update one by one. - Performance Gains: It utilizes a more efficient virtual machine within the kernel. In high-throughput environments—think 10Gbps links—the reduced CPU overhead is a massive advantage.
- Readable Syntax: The language feels like natural logic. You can group ports, IPs, and protocols into a single rule using sets and maps, reducing 50 lines of code to five.
- Unified Tooling: It merges
iptables,ip6tables, andebtablesinto one streamlined tool.
Cons
- The Learning Curve: Teams with a decade of
iptablesexperience will need a few days to adjust to the new mental model. - Tooling Updates: Older monitoring scripts or legacy IaC pipelines might still look for
iptablesoutput and will need refactoring.
A Production-Ready Architecture
When I deployed this in our environment, I ditched long, linear rule lists. Instead, I organized the configuration into functional tables. We used sets to manage server groups, such as monitoring agents or database nodes, making the system much easier to scale.
The logic hinges on a “Default Deny” policy. We block everything and only whitelist specific data paths. For a standard 3-tier app, the flow looks like this:
- Web Tier: Accepts port 443 from the public; talks to the App Tier on port 8080.
- App Tier: Accepts 8080 from the Web Tier; talks to the DB Tier on port 5432.
- DB Tier: Accepts 5432 from the App Tier; connects to nothing else.
Step-by-Step: Building Your Ruleset
Let’s build a production-grade nftables config. First, ensure the package is on your system. Use apt install nftables for Debian/Ubuntu or yum install nftables for RHEL-based systems.
1. Clearing the Slate
Start with a clean state. If you are working over SSH, be careful. Don’t set the default policy to drop until you have allowed your own IP address.
# Wipe all current rules
nft flush ruleset
2. Defining Tables and Chains
We will create a table called filter for both IPv4 and IPv6 traffic. Then, we define the standard hooks for input, forward, and output.
nft add table inet filter
nft add chain inet filter input { type filter hook input priority 0 \; policy drop \; }
nft add chain inet filter forward { type filter hook forward priority 0 \; policy drop \; }
nft add chain inet filter output { type filter hook output priority 0 \; policy accept \; }
3. Handling Essential Traffic
Never block the loopback interface. You also need to allow established connections so that response packets for your outgoing requests can get back in.
# Allow loopback interface
nft add rule inet filter input iifname "lo" accept
# Allow established and related traffic
nft add rule inet filter input ct state established,related accept
4. Organizing Tiers with Sets
Sets are the most powerful feature in nftables. Instead of writing dozens of repetitive rules, we define groups. This makes scaling from two servers to 200 effortless.
# Define sets for our infrastructure
nft add set inet filter web_servers { type ipv4_addr \; }
nft add set inet filter app_servers { type ipv4_addr \; }
nft add set inet filter db_servers { type ipv4_addr \; }
# Populate the sets with real IPs
nft add element inet filter web_servers { 10.0.1.10, 10.0.1.11 }
nft add element inet filter app_servers { 10.0.2.50, 10.0.2.51 }
nft add element inet filter db_servers { 10.0.3.100 }
5. Enforcing the Microsegmentation Logic
Now we apply the restrictions. We only allow the App servers to reach the DB servers on the PostgreSQL port (5432).
# Restrict SSH to the management subnet (e.g., your VPN)
nft add rule inet filter input ip saddr 192.168.100.0/24 tcp dport 22 accept
# Allow Web Tier to hit App Tier on 8080 (Run on App nodes)
nft add rule inet filter input ip saddr @web_servers tcp dport 8080 accept
# Allow App Tier to hit DB Tier on 5432 (Run on DB nodes)
nft add rule inet filter input ip saddr @app_servers tcp dport 5432 accept
6. Ensuring Persistence
Your rules will vanish on reboot unless you save them. On Debian or Ubuntu, the standard location is /etc/nftables.conf.
nft list ruleset > /etc/nftables.conf
systemctl enable nftables
systemctl start nftables
Long-term Maintenance and Automation
Running this setup for several months taught me that auditing becomes much simpler. When an auditor asks which services can touch the database, I just run nft list set inet filter app_servers. The output is clear and leaves no room for guesswork.
For larger fleets, don’t manage these rules manually. Integrate them into Ansible or Terraform. By defining your IP sets in a central inventory, you can push microsegmentation updates to hundreds of nodes simultaneously. This approach hardens your infrastructure and turns a complex web of connections into a manageable, documented system.

