1- # Docker Whitelist Gateway (Multi-Destination Edition)
1+ # Docker Whitelist Gateway
22
3- A transparent outbound firewall + DNS gateway for Docker internal networks.
3+ Outbound network gateway + filtered DNS for Docker internal networks.
44
5- This project allows containers attached to ** internal (isolated) Docker networks** to
6- reach selected external destinations while keeping all other outbound traffic blocked.
5+ This project allows containers attached to ** internal Docker networks** to reach only a
6+ defined set of external destinations, while preserving internal Docker service
7+ resolution.
78
8- Unlike classic single-target whitelist proxies, this implementation supports :
9+ It is designed for stacks where :
910
10- - Multiple external destinations (multi-destination)
11- - Dynamic DNS resolution
12- - Automatic subnet detection (no fixed IP configuration)
13- - Internal service resolution preservation
14- - Transparent TCP forwarding using iptables + asyncio
11+ - the main application runs on an internal network
12+ - outbound internet access must be restricted
13+ - only specific domains or IPs should be reachable
14+ - internal Docker service names must continue to resolve normally
1515
1616---
1717
18- ## Architecture Overview
18+ ## Architecture
1919
20- The gateway is designed for environments where :
20+ The setup usually uses two containers :
2121
22- - The main service runs in a Docker ** internal network**
23- - Outbound internet access must be restricted
24- - Only specific domains/IPs should be reachable
25- - Internal service discovery must keep working
22+ 1 . ** gateway container**
2623
27- The architecture typically uses two containers:
24+ - enables IPv4 forwarding
25+ - applies ` iptables ` and ` ipset ` rules
26+ - performs NAT/masquerading for outbound traffic
27+ - periodically resolves allowed hosts and populates the whitelist
2828
29- 1 . ** gateway container**
30- - Runs iptables rules
31- - Runs async TCP forwarder
32- - Runs controlled DNS (dnsmasq)
33- - Whitelists allowed destinations
34- 2 . ** net_setup sidecar (optional but recommended)**
35- - Shares network namespace with the main service
36- - Rewrites default route to gateway
37- - Rewrites /etc/resolv.conf to use gateway DNS
29+ 2 . ** net_setup sidecar**
30+ - shares the network namespace of the main app container
31+ - rewrites the default route so traffic exits through the gateway
32+ - optionally starts a ** local dnsmasq**
33+ - generates internal DNS entries automatically from Docker metadata
3834
3935---
4036
41- ## How It Works
37+ ## How it works
4238
43- ### Traffic Redirection
39+ ### Outbound traffic filtering
4440
45- The gateway:
41+ The gateway container maintains an ` ipset ` containing the IPs resolved from
42+ ` ALLOWED_HOSTS ` .
4643
47- - Installs NAT REDIRECT rules
48- - Captures outbound TCP traffic
49- - Recovers original destination using ` SO_ORIGINAL_DST `
50- - Forwards only if destination is allowed
44+ Traffic is allowed only when the destination IP matches that set.
5145
52- All other outbound traffic is dropped.
46+ The gateway also installs:
47+
48+ - a ` FORWARD ` rule allowing ` RELATED,ESTABLISHED `
49+ - a ` FORWARD ` rule allowing destinations present in the whitelist ipset
50+ - a ` POSTROUTING MASQUERADE ` rule for outbound NAT
51+
52+ This means the application can only reach external destinations whose IPs are currently
53+ present in the whitelist.
5354
5455---
5556
56- ### Controlled DNS
57+ ### DNS behavior
5758
58- When the main container switches its resolver to the gateway :
59+ There are two DNS layers :
5960
60- - Internal Docker names must still resolve
61- - External domains must be filtered
61+ #### 1. Local app DNS (inside the app namespace)
6262
63- The gateway :
63+ When ` DNS_LOCAL=1 ` is enabled in ` net_setup ` :
6464
65- - Uses Docker DNS to resolve internal services
66- - Runs dnsmasq for controlled external resolution
67- - Returns ` 0.0.0.0 ` for non-allowed domains
65+ - ` dnsmasq ` listens on ` 127.0.0.1 `
66+ - ` /etc/resolv.conf ` is rewritten to use ` 127.0.0.1 `
67+ - external resolution is restricted to domains derived from ` ALLOWED_HOSTS `
68+ - internal Docker service names are served from an autogenerated hosts file
6869
69- Allowed example:
70+ #### 2. Internal Docker service resolution
7071
71- transfer.einsamobile.de → 194.26.180.120
72+ Internal names are not maintained manually.
7273
73- Blocked example:
74+ Instead, the sidecar can generate ` /run/dnsmasq-internal.hosts ` from Docker metadata
75+ using the Docker socket. This includes:
7476
75- www.google.com → 0.0.0.0
77+ - Compose service names (` db ` , ` smtp ` , ` odoo ` , etc.)
78+ - explicit network aliases (` wdb_internal ` , etc.)
79+ - container names when useful
80+
81+ This preserves internal service discovery without relying on Docker DNS as an upstream
82+ resolver.
7683
7784---
7885
79- ## Required Capability
86+ ## Required capability
8087
8188``` yaml
8289cap_add :
8390 - NET_ADMIN
8491` ` `
8592
86- ---
87-
88- ## Environment Variables
89-
90- ### ALLOWED_HOSTS (Required)
91-
92- Space-separated list of domains or IPs allowed for outbound access.
93+ For automatic internal DNS generation, the net setup sidecar also needs access to the
94+ Docker socket:
9395
9496` ` ` yaml
95- environment :
96- ALLOWED_HOSTS : " api.example.com smtp.example.com 1.2.3.4 "
97+ volumes :
98+ - /var/run/docker.sock:/var/run/docker.sock:ro
9799` ` `
98100
99101---
100102
101- ### PORT
103+ ## Environment variables
102104
103- Default: ` *`
105+ ### Common
104106
105- Defines allowed TCP ports.
107+ #### ` ALLOWED_HOSTS` (required)
106108
107- Examples :
109+ Space-separated list of allowed external domains and/or IPs.
110+
111+ Example :
108112
109113` ` ` yaml
110- PORT: "443"
111- PORT: "80 443 8080"
112- PORT: "21 50000-51000"
114+ ALLOWED_HOSTS: "graph.microsoft.com api.example.com 1.2.3.4"
113115` ` `
114116
117+ Domains are used for :
118+
119+ - whitelist IP resolution in the gateway
120+ - filtered external DNS in local dnsmasq
121+
122+ IP literals are added directly to the whitelist.
123+
115124---
116125
117- # ## PRE_RESOLVE
126+ # ### `DNS_UPSTREAMS`
118127
119- Default : ` 0 `
128+ Default :
120129
121- When enabled (`1`) :
130+ ` ` ` yaml
131+ DNS_UPSTREAMS: "1.1.1.1 8.8.8.8"
132+ ` ` `
122133
123- - The gateway resolves allowed domains itself
124- - Refreshes periodically
125- - Avoids dependency on system DNS
134+ Upstream resolvers used by dnsmasq for allowed external domains.
126135
127136---
128137
129- # ## RESOLVE_INTERVAL
138+ # ## Gateway mode
130139
131- Default : ` 60 `
140+ # ### `MODE_RUN=gateway `
132141
133- DNS refresh interval in seconds when `PRE_RESOLVE=1` .
142+ Runs the container as the outbound network gateway .
134143
135- ---
144+ Optional :
136145
137- # ## DNS_UPSTREAMS
138-
139- Default : ` 1.1.1.1 8.8.8.8`
146+ ` ` ` yaml
147+ IPSET_NAME: "whitelist"
148+ REFRESH_INTERVAL: "300"
149+ ` ` `
140150
141- Upstream DNS servers used by dnsmasq.
151+ - `IPSET_NAME` : name of the whitelist ipset
152+ - `REFRESH_INTERVAL` : refresh interval in seconds for resolving allowed hosts
142153
143154---
144155
145- # ## DNS_INTERNAL_NAMES
156+ # ## Net setup mode
146157
147- Optional.
158+ # ### `MODE_RUN=net_setup`
148159
149- Defines internal Docker service names that must always resolve .
160+ Runs the container as the sidecar that configures routing and DNS for the main app .
150161
151- Example :
162+ Required :
163+
164+ ` ` ` yaml
165+ GATEWAY_NAME: "proxy_general"
166+ ` ` `
167+
168+ This must resolve to the gateway container on the shared Docker network.
169+
170+ Optional :
152171
153172` ` ` yaml
154- DNS_INTERNAL_NAMES: "db smtp redis backend gateway"
173+ DNS_LOCAL: "1"
174+ DNS_INTERNAL_FROM_DOCKER: "1"
175+ DOCKER_PROJECT: "my-project"
176+ DOCKER_PRIMARY_SERVICE: "odoo"
177+ DNS_DOCKER_REFRESH_INTERVAL: "5"
155178` ` `
156179
157- Only required if the gateway replaces Docker's default resolver and you want
158- deterministic internal resolution.
180+ - `DNS_LOCAL=1` : enable local dnsmasq in the app namespace
181+ - `DNS_INTERNAL_FROM_DOCKER=1` : generate internal DNS entries from Docker metadata
182+ - `DOCKER_PROJECT` : Compose project name used to discover related containers
183+ - `DOCKER_PRIMARY_SERVICE` : main service whose visible networks define the DNS scope
184+ - `DNS_DOCKER_REFRESH_INTERVAL` : refresh interval for autogenerated internal hosts
159185
160186---
161187
162- # # Multi-Destination Example (Generic Service)
188+ # # Example
163189
164190` ` ` yaml
165191services:
166192 app:
167193 image: my-app
168194 networks:
169195 - internal
170- cap_add:
171- - NET_ADMIN
172196 depends_on:
173197 - gateway
174198
@@ -177,9 +201,16 @@ services:
177201 network_mode: "service:app"
178202 cap_add:
179203 - NET_ADMIN
204+ volumes:
205+ - /var/run/docker.sock:/var/run/docker.sock:ro
180206 environment:
181- MODE_RUN: net_setup
182- GATEWAY_NAME: gateway
207+ MODE_RUN: "net_setup"
208+ GATEWAY_NAME: "gateway"
209+ DNS_LOCAL: "1"
210+ DNS_INTERNAL_FROM_DOCKER: "1"
211+ DOCKER_PROJECT: "my-project"
212+ DOCKER_PRIMARY_SERVICE: "app"
213+ ALLOWED_HOSTS: "graph.microsoft.com api.example.com"
183214
184215 gateway:
185216 image: docker-whitelist-gateway:latest
@@ -189,8 +220,8 @@ services:
189220 - internal
190221 - public
191222 environment:
192- ALLOWED_HOSTS : "api.stripe.com smtp.mailgun.org ftp.partner.com "
193- PRE_RESOLVE: 1
223+ MODE_RUN : "gateway "
224+ ALLOWED_HOSTS: "graph.microsoft.com api.example.com"
194225
195226networks:
196227 internal:
@@ -200,15 +231,21 @@ networks:
200231
201232---
202233
203- # # Summary
234+ # # Current behavior summary
204235
205- Docker Whitelist Gateway provides :
236+ This implementation now provides :
237+
238+ - outbound filtering based on destination IP whitelist
239+ - automatic refresh of allowed destination IPs
240+ - local filtered DNS for the application
241+ - automatic internal Docker name resolution from Docker metadata
242+ - no need to maintain `DNS_INTERNAL_NAMES` manually in normal setups
243+
244+ ---
206245
207- - Transparent outbound filtering
208- - DNS-level control
209- - Multi-destination support
210- - Automatic dynamic routing
211- - Clean separation between service and firewall logic
246+ # # Notes
212247
213- It is designed for secure containerized environments where outbound access must be
214- explicitly controlled.
248+ - The gateway healthcheck validates the current `iptables`/`ipset`-based setup.
249+ - Internal DNS generation depends on the Docker socket and Compose metadata.
250+ - External DNS resolution is intentionally restricted to domains derived from
251+ ` ALLOWED_HOSTS` .
0 commit comments