Docs: DSM firewall blocks container-to-container traffic

Diagnosed on a real Synology: with the DSM firewall enabled, iptables reads

    -A FORWARD -j FORWARD_FIREWALL      <- DSM, first, default deny
    -A FORWARD -j DEFAULT_FORWARD       <- Docker's ACCEPT rules, never reached

and bridge-nf-call-iptables=1 sends container-to-container packets through
FORWARD. A new connection to postgres:5432 matches no DSM allow rule and is
dropped before Docker's own "-i docker-xxx -o docker-xxx -j ACCEPT" is
consulted. RELATED,ESTABLISHED is accepted, which is why the port could test
open from a throwaway container while every new connection timed out — the
symptom looked intermittent and pointed nowhere near the firewall.

- docs §8: the diagnosis, the one-line fix
  (iptables -I FORWARD 1 -i docker+ -o docker+ -j ACCEPT), a boot-up Triggered
  Task so it survives reboots and firewall reloads, and a verification command.
- The migrate failure message now names this cause and the fix, since that is
  the error an operator actually sees.

Nothing in the stack is at fault here; this is host firewall configuration.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Nux3LCu4zP9nih4BRhPyHw
This commit is contained in:
2026-09-14 21:10:37 +02:00
co-authored by Claude Opus 5
parent 5c59eecff2
commit d29c46c229
2 changed files with 67 additions and 2 deletions
+58 -2
View File
@@ -401,7 +401,62 @@ backends, or search ever looks stale, just rebuild it:
---
## 8. Troubleshooting
## 8. DSM firewall blocks container-to-container traffic
**Symptom:** everything looks healthy, but `migrate` fails with an asyncpg
`TimeoutError`, or any service times out reaching `postgres:5432`. From a
throwaway container the port may even test open — because already-established
flows are allowed while new ones are dropped.
**Cause:** if Synology's firewall is enabled, DSM inserts its own chain ahead of
Docker's:
```
-A FORWARD -j FORWARD_FIREWALL <- DSM firewall, runs first, denies by default
-A FORWARD -j DEFAULT_FORWARD <- Docker's ACCEPT rules, never reached
```
With `bridge-nf-call-iptables=1` (the DSM default), traffic between two
containers on the same bridge traverses `FORWARD` — so it hits DSM's firewall,
matches no allow rule (your database port is not in DSM's service list, and
should not be), and is dropped before Docker's
`-i docker-xxx -o docker-xxx -j ACCEPT` is consulted.
**Fix** — allow Docker bridges to talk to themselves, ahead of the firewall:
```bash
iptables -I FORWARD 1 -i docker+ -o docker+ -j ACCEPT
```
`docker+` matches any interface whose name starts with `docker`, so it keeps
working when the bridge is recreated under a new name. It permits traffic only
*between* Docker bridges — nothing from the LAN or the internet gains access.
**Make it survive a reboot.** The rule is lost on restart and when DSM reloads
its firewall. Add a *triggered* task in *Control Panel → Task Scheduler →
Create → Triggered Task → User-defined script*, event **Boot-up**, user
**root**:
```bash
sleep 60
iptables -C FORWARD -i docker+ -o docker+ -j ACCEPT 2>/dev/null \
|| iptables -I FORWARD 1 -i docker+ -o docker+ -j ACCEPT
```
The `sleep` lets DSM's firewall finish loading first; `-C` checks for the rule
so running it twice is harmless. Re-run the same script by hand after any
change to the firewall settings.
**Verify it:**
```bash
docker run --rm --network webmail-prod postgres:16 \
pg_isready -h postgres -p 5432 # expect: accepting connections
```
---
## 9. Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
@@ -409,6 +464,7 @@ backends, or search ever looks stale, just rebuild it:
| UI loads, no live updates | WebSocket not enabled in the NAS reverse proxy | §5.2 Custom Header → WebSocket |
| Login works, logs out on reload | HTTPS terminated upstream but `X-Forwarded-Proto` stripped | Use the NAS reverse proxy as documented; don't strip headers |
| "port is already allocated" | 8088 taken by another app | change `WEB_PORT` |
| `migrate` fails with asyncpg TimeoutError | DSM firewall dropping container traffic (§8) | `iptables -I FORWARD 1 -i docker+ -o docker+ -j ACCEPT` |
| Mail not syncing | worker stopped, or credentials rejected | `./deploy/webmail.sh logs worker-sync` |
| Search finds nothing after a restore | index not rebuilt | `./deploy/webmail.sh reindex --purge` |
| App won't start after editing `.env` | malformed `MASTER_KEY` | `./deploy/webmail.sh doctor` |
@@ -418,7 +474,7 @@ per container, so they can't fill the volume.
---
## 9. Command reference
## 10. Command reference
Run these as root (`sudo -i` first): the env file is mode 600 and the data
directories are root-owned.