Scenario
A Docker container exposes a service on a specific port. The goal is to restrict access to this port so that only specific remote hosts are permitted, while all others are denied.
Background: Traffic Flow in Docker
When Docker runs, it modifies the host's iptables rules. External traffic destined for a container does not pass through the standard INPUT chain in the filter table. Instead, it is processed as follows:
- NAT Handling: In the
nattable, thePREROUTINGchain redirects traffic to theDOCKERchain. TheDOCKERchain usesDNAT(Destination Network Address Translation) to forward the request to the container's internal IP address. - Filter Handling: Because the destination IP changes during the NAT phase, the packet moves to the
FORWARDchain in thefiltertable rather then theINPUTchain.
Analyzing the FORWARD Chain
When inspecting the filter table, the FORWARD chain delegates control to several custom chains created by Docker:
- DOCKER-ISOLATION-STAGE-1 / STAGE-2: These handle internal network segmentation between containers.
- DOCKER: Contains rules automatically generated by Docker to allow traffic to sepcific containers.
- DOCKER-USER: This is a reserved chain existing specifically for administrators to insert custom firewall rules.
Since iptables processes rules sequentially and stops processing a chain when a match is found, the DOCKER-USER chain is the ideal place to enforce custom accesss policies. Rules here are evaluated before Docker's automatically generated rules.
Implementation
To restrict access to a container service (e.g., running on port 80) so that only a specific IP (10.10.181.201) can connect, insert rules into the DOCKER-USER chain.
First, insert a rule at the top to explicitly allow the trusted source:
iptables -I DOCKER-USER -s 10.10.181.201 -p tcp --dport 80 -j ACCEPT
Next, insert a rule immediately after (or before, depending on insertion order) to drop traffic from all other sources to that port:
iptables -I DOCKER-USER -p tcp --dport 80 -j DROP
Note: The order matters. In the example above using -I (insert), the DROP rule is inserted first, and then the ACCEPT rule for the specific IP is inserted before it, ensuring the specific IP is allowed before the generic drop catches the rest.