Dokploy / Dokploy/dokploy

RFC: IP Allow-List Filtering for Exposed Ports

Open
#4,048 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
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:

  1. Docker bypasses host firewalls — published ports go through the FORWARD chain, so ufw/firewalld rules are ignored (Docker docs)
  2. Swarm ingress SNATs traffic — in ingress mode, 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)
  3. 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_list table: each port (application or database) can have multiple allowed CIDRs
  • Dokploy generates iptables rules in a custom DOKPLOY-FILTER chain (sub-chain of DOCKER-USER)
  • Rules are applied locally via execAsync and 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 host mode. Auto-switch ports to host when IP filtering is enabled (loses routing mesh load balancing).
  • B) Support both modes but warn users that ingress mode IP filtering relies on conntrack and 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 ports table) — 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 + databaseType columns (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 ports table for databases too, then just use portId everywhere (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-restore for 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.