Skip to content

Network & Firewall Integrations ​

TCP Warden acts as an intelligent Layer 4 reverse proxy and protocol firewall. In order to inspect, rate-limit, and ban abusive clients, incoming traffic must pass through TCP Warden before reaching your backend services.

There are four primary architectures to achieve this:

Pattern 1: nftables Redirection (Modern Linux Default)Client ──► [eth0 : Port 22] ──(nft PREROUTING)──► RouteWarden (:2222) ──► sshd (127.0.0.1:22)Pattern 2: iptables / UFW / firewalld (Legacy / Cloud VMs)Client ──► [eth0 : Port 25] ──(iptables REDIRECT)──► RouteWarden (:2525) ──► postfix (127.0.0.1:25)Pattern 3: Direct Port Swapping (Reverse Proxy)Client ──► [Standard Port 5432] ──► RouteWarden (:5432) ──► postgres (127.0.0.1:5433)Pattern 4: Docker Network Isolation (Containerized Stacks)Client ──► [Host Port 6379] ──► RouteWarden ──(Internal Bridge)──► redis-server (no host ports)

Architecture Comparison ​

MethodModifies Backend Config?Requires Firewall Rules?Client IP Preserved?Recommended For
nftablesNo (Zero touch)Yes (nft add rule)YesModern Linux (Debian 10+, Ubuntu 20+, RHEL 8+)
iptablesNo (Zero touch)Yes (iptables -t nat)YesLegacy Linux systems & Cloud VMs
Port SwappingYes (Move backend to loopback)NoYes (via L4 proxying)Bare-metal environments without NAT access
Docker IsolationNo (Zero touch)NoYesContainerized stacks & Docker Compose

Strategy 1: nftables Redirection (Modern Linux Default) ​

nftables provides kernel-level packet redirection with minimal overhead. Your backend service continues listening on its default port on 127.0.0.1. The kernel intercepts incoming traffic on external network interfaces and redirects it to TCP Warden.

Port Range Recommendation: Privileged vs Unprivileged Ports

  • Privileged Ports (<= 1024): Services like SSH (22), SMTP (25), POP3 (110), IMAP (143), FTP (21), and HTTP/TLS (80 / 443) benefit most from nftables/iptables redirection because existing system daemons typically run as root on standard ports. Redirection routes traffic through TCP Warden without reconfiguring system services.
  • Unprivileged Ports (> 1024): Services like PostgreSQL (5432), MySQL (3306), Redis (6379), MongoDB (27017), AMQP (5672), and MQTT (1883) do not require firewall rules. You can simply bind TCP Warden directly to the standard port as a transparent reverse proxy (Strategy 3) or isolate them within a Docker network (Strategy 4).

CRITICAL: LOOPBACK RECURSION PREVENTION

Always include iifname "eth0" (your external interface). Never redirect traffic on the loopback interface (lo).

When TCP Warden forwards approved connections to 127.0.0.1:22, the packet travels over lo. If the redirect rule matched all interfaces without iifname, TCP Warden's own connection would be caught and redirected back to TCP Warden, creating an infinite connection loop.

# 1. Create a dedicated NAT table for TCP Wardensudo nft add table ip routewarden_nat# 2. Add the prerouting chainsudo nft add chain ip routewarden_nat prerouting '{ type nat hook prerouting priority dstnat; policy accept; }'

Strategy 2: iptables, UFW, & firewalld Redirection ​

For hosts running legacy iptables, Ubuntu UFW, or RHEL/CentOS firewalld:

# SSH: Redirect external port 22 -> TCP Warden port 2222sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 22 -j REDIRECT --to-port 2222# SMTP: Redirect external port 25 -> TCP Warden port 2525sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 25 -j REDIRECT --to-port 2525# POP3: Redirect external port 110 -> TCP Warden port 1110sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 110 -j REDIRECT --to-port 1110# IMAP: Redirect external port 143 -> TCP Warden port 1143sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 143 -j REDIRECT --to-port 1143# FTP Control: Redirect external port 21 -> TCP Warden port 2121sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 21 -j REDIRECT --to-port 2121# TLS SNI / HTTPS: Redirect external port 443 -> TCP Warden port 8443sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 443 -j REDIRECT --to-port 8443# HTTP: Redirect external port 80 -> TCP Warden port 8081sudo iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8081

Strategy 3: Direct Port Swapping (Backend Reconfiguration) ​

If you prefer TCP Warden to bind directly to standard ports (22, 5432, 6379, 3306) without using firewall NAT rules, update your backend daemon configurations so they release the standard port and bind strictly to loopback:

ServiceConfiguration FileRequired Setting UpdateService Restart Command
SSH/etc/ssh/sshd_configPort 22222
ListenAddress 127.0.0.1
sudo systemctl restart sshd
SMTP (Postfix)/etc/postfix/master.cf127.0.0.1:2525 inet n - y - - smtpdsudo systemctl restart postfix
POP3 & IMAP (Dovecot)/etc/dovecot/conf.d/10-master.confinet_listener imap { port = 1144 }
inet_listener pop3 { port = 1111 }
sudo systemctl restart dovecot
PostgreSQL/etc/postgresql/<ver>/main/postgresql.confport = 5433
listen_addresses = '127.0.0.1'
sudo systemctl restart postgresql
MySQL / MariaDB/etc/mysql/my.cnfport = 3307
bind-address = 127.0.0.1
sudo systemctl restart mysql
Redis/etc/redis/redis.confport 6380
bind 127.0.0.1
sudo systemctl restart redis-server
MongoDB/etc/mongod.confnet.port: 27018
net.bindIp: 127.0.0.1
sudo systemctl restart mongod
RabbitMQ/etc/rabbitmq/rabbitmq.conflisteners.tcp.default = 127.0.0.1:5674sudo systemctl restart rabbitmq-server
OpenLDAP/etc/default/slapdSLAPD_SERVICES="ldap://127.0.0.1:3890/"sudo systemctl restart slapd
FTP (vsftpd)/etc/vsftpd.conflisten_port=2122
listen_address=127.0.0.1
sudo systemctl restart vsftpd

BIND ADDRESS REQUIREMENT

Always bind the backend service to 127.0.0.1 (loopback). If your backend service binds to 0.0.0.0 on its internal port, external attackers can bypass TCP Warden entirely by scanning and connecting directly to that port.

# 1. Edit /etc/ssh/sshd_config:# Port 22222# ListenAddress 127.0.0.1# 2. Restart sshd daemonsudo systemctl restart sshd

Strategy 4: Docker Compose Network Isolation ​

In containerized deployments, the backend database or service container should never publish ports to the host. Only TCP Warden exposes ports:

services:  tcp-warden:    image: ghcr.io/routewarden/tcp-warden:latest    container_name: tcp-warden    restart: unless-stopped    ports:      # Public listener ports (protected by TCP Warden)      - "22:2222"       # SSH Guard      - "80:8081"       # HTTP Protocol Guard      - "443:8443"      # TLS SNI Guard      - "25:2525"       # SMTP Mail Guard      - "587:5870"      # SMTP Submission Guard      - "143:1143"      # IMAP4 Mail Guard      - "993:9930"      # IMAPS Guard      - "389:1390"      # LDAP Guard      - "636:1636"      # LDAPS Guard      - "5432:5432"     # PostgreSQL Bastion      - "6379:6379"     # Redis Guard      - "9091:9091"     # TCP Warden Management API    volumes:      - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro      - tcp-warden-data:/var/lib/routewarden    networks:      - internal-net  postgres-backend:    image: postgres:16-alpine    environment:      POSTGRES_USER: appuser      POSTGRES_PASSWORD: secretpassword      POSTGRES_DB: appdb    # Notice: NO host port publication! Only accessible by tcp-warden    networks:      - internal-net  redis-backend:    image: redis:7-alpine    command: ["redis-server", "--requirepass", "secretpassword"]    # Notice: NO host port publication! Only accessible by tcp-warden    networks:      - internal-netnetworks:  internal-net:    driver: bridgevolumes:  tcp-warden-data:

Privileged Ports (< 1024) & Capabilities ​

On Linux systems, binding directly to ports below 1024 (22 for SSH, 25 for SMTP, 80 for HTTP, 443 for HTTPS) normally requires root privileges.

When using Docker port mappings like ports: - "22:2222", the host Docker daemon binds port 22 on the host, while TCP Warden runs safely as non-root routewarden (UID 1000) inside the container. No root permissions are needed.

2. In Docker: Direct Privileged Port Binding (ports: - "22:22") ​

If you configure TCP Warden to bind directly to :22 inside the container, grant the capability in docker-compose.yml or set TARGET_USER=root.

3. Outside Docker: Bare-Metal Linux (Systemd Service) ​

To bind privileged ports without running the daemon as root, configure AmbientCapabilities=CAP_NET_BIND_SERVICE:

# /etc/systemd/system/tcp-warden.service[Unit]Description=TCP Warden L4 Security ProxyAfter=network.target[Service]User=routewardenGroup=routewardenAmbientCapabilities=CAP_NET_BIND_SERVICECapabilityBoundingSet=CAP_NET_BIND_SERVICEExecStart=/usr/local/bin/tcp-warden run --config /etc/routewarden/tcp-warden.yamlRestart=alwaysRestartSec=3[Install]WantedBy=multi-user.target

Verification & Troubleshooting Checklist ​

After configuring firewall redirection or port swapping, run through this checklist to ensure your setup is functioning correctly:

# Check that TCP Warden and backends listen on distinct portsss -tlpn | grep -E ':(22|2222|5432|5433|6379|6380)'

Released under the MIT License.