RFC: IP Allow-List Filtering for Exposed Ports
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 37.4k
- Forks
- 3k
- Avg merge
- 1d 3h
- Merged PRs (30d)
- 73
Description
Problem
Dokploy allows exposing ports for applications and databases, but there is no way to restrict access by source IP. This is a common security need — for example, restricting database ports to office IPs only.
Docker Swarm makes this notoriously difficult:
- Docker bypasses host firewalls — published ports go through the
FORWARDchain, soufw/firewalldrules are ignored (Docker docs) - Swarm ingress SNATs traffic — in
ingressmode, real client IPs are replaced with internal swarm IPs (10.255.0.x), making IP-based filtering impossible at the application/proxy level (moby/moby #25526) - No built-in solution — Docker provides no native per-port IP filtering
Proposed Solution
Add per-port IP allow-list filtering using iptables DOCKER-USER chain, managed entirely by Dokploy. This works at the kernel level with zero overhead (no proxy, no sidecar).
How it would work
- New
ip_allow_listtable: each port (application or database) can have multiple allowed CIDRs - Dokploy generates iptables rules in a custom
DOKPLOY-FILTERchain (sub-chain ofDOCKER-USER) - Rules are applied locally via
execAsyncand on remote swarm nodes via SSH - Empty allow-list = port open to all (backward-compatible)
- Changes apply immediately — no redeploy needed
Example rules
# Host mode — straightforward
iptables -A DOKPLOY-FILTER -p tcp --dport 5432 -s 10.0.0.0/8 -j RETURN
iptables -A DOKPLOY-FILTER -p tcp --dport 5432 -j DROP
# Ingress mode — needs conntrack for port matching after DNAT
iptables -A DOKPLOY-FILTER -p tcp -m conntrack --ctorigdstport 8080 -s 1.2.3.0/24 -j RETURN
iptables -A DOKPLOY-FILTER -p tcp -m conntrack --ctorigdstport 8080 -j DROP
Key Design Decisions — Request for Comments
I'd like community input on these decisions before implementation:
1. Ingress mode: is IP filtering even possible?
In Docker Swarm ingress mode, source IPs are SNATed to internal addresses before packets reach the DOCKER-USER chain. This means -s <real-client-ip> won't match.
Options:
- A) Only support IP filtering in
hostmode. Auto-switch ports tohostwhen IP filtering is enabled (loses routing mesh load balancing). - B) Support both modes but warn users that ingress mode IP filtering relies on
conntrackand may not preserve real source IPs on all setups. - C) Use the docker-ingress-routing-daemon approach to recover real IPs in ingress mode (adds complexity).
This is the biggest open question. If SNAT makes ingress-mode filtering impossible, the feature should either force host mode or clearly document the limitation.
2. Scope: which resources should support IP filtering?
- Applications (per-port via existing
portstable) — seems obvious - Databases (Postgres, MySQL, MariaDB, MongoDB, Redis via
externalPort) — arguably the highest-value target - Compose services — ports are user-defined in compose files, much harder to intercept
Current thinking: Applications + Databases. Compose out of scope. Thoughts?
3. Data model for database allow-lists
Application ports have a clean FK (portId). Databases have externalPort as a direct integer field on 5 separate schemas. Options:
- A) Polymorphic
databaseId+databaseTypecolumns (simple but no FK constraint, risk of orphaned rows) - B) 5 explicit nullable FK columns (
postgresId,mysqlId, etc.) with cascade delete (safe but verbose) - C) Add a unified
portstable for databases too, then just useportIdeverywhere (cleanest but bigger refactor)
4. Rule persistence without external services
Current plan: Dokploy applies rules on startup, on CRUD changes, on deploy, and via a periodic reconciliation interval (every ~5 minutes). No systemd services or scripts on disk — all state lives in the DB.
Concerns:
- Brief unprotected window during flush-and-rebuild. Should we use
iptables-restorefor atomic swaps? - Race conditions between concurrent applies. Need a mutex?
- If Dokploy crashes, DROP rules persist (could lock users out). Should there be a cleanup on shutdown?
5. IPv6 support
The iptables approach is IPv4-only. Should we also generate ip6tables rules, or declare IPv6 out of scope initially?
6. Allow-list only, or also deny-list?
Current plan: allow-list only (empty = open to all). This follows least-privilege. Is deny-list support needed?
7. UI placement
Current plan: a "shield" icon on each port row that opens an IP allow-list dialog. Databases get the same UI when externalPort is set. Any UX preferences?
References
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
No implementation files or tests are named. Start by reviewing the proposed ip_allow_list table, existing ports table, database schemas, execAsync entry point, and DOCKER-USER integration, then resolve the ingress, scope, persistence, IPv6, and UI questions. Done means an agreed feature scope and design ready for implementation.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, mariadb, mongodb, mysql, postgresql, redis
- Domain
- databases, infrastructure, security
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 25/100