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:
@@ -69,6 +69,15 @@ print(
|
||||
f"Last error: {type(last_error).__name__}: {last_error}",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print(
|
||||
"[entrypoint] If this is a Synology NAS: DSM's firewall filters traffic "
|
||||
"between containers (FORWARD -j FORWARD_FIREWALL runs before Docker's own "
|
||||
"rules and denies by default). Fix:\n"
|
||||
" iptables -I FORWARD 1 -i docker+ -o docker+ -j ACCEPT\n"
|
||||
"See docs/nas-install.md, 'DSM firewall blocks container-to-container "
|
||||
"traffic'.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
}
|
||||
|
||||
+58
-2
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user