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}",
|
f"Last error: {type(last_error).__name__}: {last_error}",
|
||||||
file=sys.stderr,
|
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)
|
sys.exit(1)
|
||||||
PYEOF
|
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 |
|
| 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 |
|
| 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 |
|
| 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` |
|
| "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` |
|
| 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` |
|
| 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` |
|
| 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
|
Run these as root (`sudo -i` first): the env file is mode 600 and the data
|
||||||
directories are root-owned.
|
directories are root-owned.
|
||||||
|
|||||||
Reference in New Issue
Block a user