fix(security): close exposed PostgreSQL and Redis ports (BSI/CERT-Bund report)
Some checks failed
Some checks failed
Docker bypasses UFW iptables, so bare port mappings like "5432:5432" exposed the database to the public internet. Removed port mappings for PostgreSQL and Redis (they only need Docker-internal networking), and bound the API port to 127.0.0.1 since only Caddy needs to reach it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1706,6 +1706,34 @@ curl -s http://localhost:9090/api/v1/rules | python3 -m json.tool | grep -i redi
|
||||
|
||||
Docker network segmentation, fail2ban configuration, and automatic security updates.
|
||||
|
||||
### 20.0 Docker Port Binding (Critical — Docker Bypasses UFW)
|
||||
|
||||
!!! danger "Docker bypasses UFW firewall rules"
|
||||
Docker manipulates iptables directly, bypassing UFW entirely. A port mapping like `"5432:5432"` exposes PostgreSQL to the **public internet** even if UFW only allows ports 22, 80, and 443. This was flagged by the German Federal Office for Information Security (BSI/CERT-Bund) in March 2026.
|
||||
|
||||
**Rules for port mappings in `docker-compose.yml`:**
|
||||
|
||||
1. **No port mapping** for services that only talk to other containers (PostgreSQL, Redis) — they communicate via Docker's internal network using service names (`db:5432`, `redis:6379`)
|
||||
2. **Bind to `127.0.0.1`** for services that need host access but not internet access (API via Caddy, Flower, Prometheus, Grafana, etc.)
|
||||
3. **Never use bare port mappings** like `"5432:5432"` or `"6380:6379"` — these bind to `0.0.0.0` (all interfaces)
|
||||
|
||||
| Service | Correct | Wrong |
|
||||
|---|---|---|
|
||||
| PostgreSQL | *(no ports section)* | `"5432:5432"` |
|
||||
| Redis | *(no ports section)* | `"6380:6379"` |
|
||||
| API | `"127.0.0.1:8001:8000"` | `"8001:8000"` |
|
||||
| Flower | `"127.0.0.1:5555:5555"` | `"5555:5555"` |
|
||||
|
||||
**After deploying, verify no services are exposed:**
|
||||
|
||||
```bash
|
||||
# Should return nothing for 5432 and 6379
|
||||
sudo ss -tlnp | grep -E '0.0.0.0:(5432|6379|6380)'
|
||||
|
||||
# Should show 127.0.0.1 only for app services
|
||||
sudo ss -tlnp | grep -E '(8001|5555|9090|3001)'
|
||||
```
|
||||
|
||||
### 20.1 Docker Network Segmentation
|
||||
|
||||
Three isolated networks replace the default flat network:
|
||||
@@ -2340,12 +2368,12 @@ After Google Wallet is verified working:
|
||||
|
||||
| Service | Internal Port | External Port | Domain (via Caddy) |
|
||||
|---|---|---|---|
|
||||
| Orion API | 8000 | 8001 | `api.wizard.lu` |
|
||||
| Main Platform | 8000 | 8001 | `wizard.lu` |
|
||||
| OMS Platform | 8000 | 8001 | `omsflow.lu` |
|
||||
| Loyalty+ Platform | 8000 | 8001 | `rewardflow.lu` |
|
||||
| PostgreSQL | 5432 | 5432 | (internal only) |
|
||||
| Redis | 6379 | 6380 | (internal only) |
|
||||
| Orion API | 8000 | 127.0.0.1:8001 | `api.wizard.lu` |
|
||||
| Main Platform | 8000 | 127.0.0.1:8001 | `wizard.lu` |
|
||||
| OMS Platform | 8000 | 127.0.0.1:8001 | `omsflow.lu` |
|
||||
| Loyalty+ Platform | 8000 | 127.0.0.1:8001 | `rewardflow.lu` |
|
||||
| PostgreSQL | 5432 | none (Docker internal) | (internal only) |
|
||||
| Redis | 6379 | none (Docker internal) | (internal only) |
|
||||
| Flower | 5555 | 5555 | `flower.wizard.lu` |
|
||||
| Gitea | 3000 | 3000 | `git.wizard.lu` |
|
||||
| Prometheus | 9090 | 9090 (localhost) | (internal only) |
|
||||
|
||||
Reference in New Issue
Block a user