Reformatting README
This commit is contained in:
84
README.md
84
README.md
@@ -1,49 +1,37 @@
|
|||||||
# ddev-tailscale-docker-fix
|
# Docker + Tailscale Network Fix
|
||||||
|
|
||||||
> **Note:** these scripts were written by an LLM (Claude Code), diagnosing
|
> **Note:** these scripts were written by an LLM (Claude Code), diagnosing and fixing an issue live on one specific
|
||||||
> and fixing an issue live on one specific machine (Fedora + NetworkManager
|
> machine (Fedora + NetworkManager + Tailscale + Docker). They worked there, but review them yourself before running as
|
||||||
> + Tailscale + Docker). They worked there, but review them yourself before
|
> root on another machine — network/firewall behavior varies across distros, Tailscale versions, and configurations.
|
||||||
> running as root on another machine — network/firewall behavior varies
|
|
||||||
> across distros, Tailscale versions, and configurations.
|
|
||||||
|
|
||||||
Fixes Docker containers (e.g. ddev) losing internet access when Tailscale
|
Fixes Docker containers (e.g. ddev) losing internet access when Tailscale is running with an exit node ("route all
|
||||||
is running with an exit node ("route all traffic") enabled.
|
traffic") enabled.
|
||||||
|
|
||||||
## The problem
|
## The problem
|
||||||
|
|
||||||
With an exit node and "route all traffic" enabled, Tailscale installs a
|
With an exit node and "route all traffic" enabled, Tailscale installs a catch-all `ip rule` that captures every packet
|
||||||
catch-all `ip rule` that captures every packet not explicitly exempted
|
not explicitly exempted and routes it out via the `tailscale0` interface. Tailscale exempts its own daemon traffic from
|
||||||
and routes it out via the `tailscale0` interface. Tailscale exempts its
|
that capture with a firewall mark, but has no way to exempt traffic that only passes *through* the host (forwarded)
|
||||||
own daemon traffic from that capture with a firewall mark, but has no
|
|
||||||
way to exempt traffic that only passes *through* the host (forwarded)
|
|
||||||
rather than originating from it.
|
rather than originating from it.
|
||||||
|
|
||||||
Docker containers' outbound traffic is exactly that: forwarded, not
|
Docker containers' outbound traffic is exactly that: forwarded, not locally-originated. It gets no exemption, so it's
|
||||||
locally-originated. It gets no exemption, so it's swept into
|
swept into Tailscale's catch-all route, sent out via `tailscale0`, and dropped by Tailscale's own anti-spoofing firewall
|
||||||
Tailscale's catch-all route, sent out via `tailscale0`, and dropped by
|
rule (`ts-forward`) because it isn't a recognized Tailscale-tunneled flow.
|
||||||
Tailscale's own anti-spoofing firewall rule (`ts-forward`) because it
|
|
||||||
isn't a recognized Tailscale-tunneled flow.
|
|
||||||
|
|
||||||
Symptom: every DNS lookup / outbound HTTP request from inside a
|
Symptom: every DNS lookup / outbound HTTP request from inside a container hangs until timeout whenever Tailscale is
|
||||||
container hangs until timeout whenever Tailscale is connected — WP core
|
connected — WP core update checks, plugin license pings, `composer`/`npm` installs, anything. The host's own
|
||||||
update checks, plugin license pings, `composer`/`npm` installs,
|
applications aren't affected, since their traffic is locally-originated and gets Tailscale's exemption mark.
|
||||||
anything. The host's own applications aren't affected, since their
|
|
||||||
traffic is locally-originated and gets Tailscale's exemption mark.
|
|
||||||
|
|
||||||
## The fix
|
## The fix
|
||||||
|
|
||||||
A higher-priority `ip rule` that routes Docker's private bridge subnet
|
A higher-priority `ip rule` that routes Docker's private bridge subnet (default: `172.16.0.0/12`, covering Docker's
|
||||||
(default: `172.16.0.0/12`, covering Docker's default address pool)
|
default address pool) through the normal routing table instead of Tailscale's catch-all. This doesn't weaken the VPN for
|
||||||
through the normal routing table instead of Tailscale's catch-all. This
|
anything else on the host — it only affects packets forwarded from Docker's own bridge networks.
|
||||||
doesn't weaken the VPN for anything else on the host — it only affects
|
|
||||||
packets forwarded from Docker's own bridge networks.
|
|
||||||
|
|
||||||
The rule is kept in place by a systemd timer that re-checks/re-adds it
|
The rule is kept in place by a systemd timer that re-checks/re-adds it every 15s (and 5s after boot), rather than hooked
|
||||||
every 15s (and 5s after boot), rather than hooked to a network event:
|
to a network event: `tailscale0` is a tun device created directly by `tailscaled`, not through a NetworkManager
|
||||||
`tailscale0` is a tun device created directly by `tailscaled`, not
|
connection profile, so NetworkManager never fires dispatcher/udev events we could reliably hook into for it (confirmed
|
||||||
through a NetworkManager connection profile, so NetworkManager never
|
by testing — don't waste time re-trying that route).
|
||||||
fires dispatcher/udev events we could reliably hook into for it
|
|
||||||
(confirmed by testing — don't waste time re-trying that route).
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
@@ -51,9 +39,8 @@ fires dispatcher/udev events we could reliably hook into for it
|
|||||||
sudo ./install.sh
|
sudo ./install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
Optional overrides if Docker's default address pool doesn't apply on
|
Optional overrides if Docker's default address pool doesn't apply on your machine (e.g. you have 15+ concurrent Docker
|
||||||
your machine (e.g. you have 15+ concurrent Docker networks and Docker
|
networks and Docker has fallen back to a different private range):
|
||||||
has fallen back to a different private range):
|
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo ./install.sh --subnet 172.16.0.0/12 --priority 5200 --table main
|
sudo ./install.sh --subnet 172.16.0.0/12 --priority 5200 --table main
|
||||||
@@ -74,17 +61,12 @@ sudo ./uninstall.sh
|
|||||||
|
|
||||||
## Caveats
|
## Caveats
|
||||||
|
|
||||||
- Only covers Docker networks within `172.16.0.0/12`. If you have more
|
- Only covers Docker networks within `172.16.0.0/12`. If you have more than ~15 concurrent Docker networks on one
|
||||||
than ~15 concurrent Docker networks on one machine, Docker falls back
|
machine, Docker falls back to `192.168.0.0/20` blocks and then `10.0.0.0/8` — those aren't covered by default. Don't
|
||||||
to `192.168.0.0/20` blocks and then `10.0.0.0/8` — those aren't
|
just widen the subnet to `192.168.0.0/16` or `10.0.0.0/8` to compensate: those ranges are also real LAN/VPN
|
||||||
covered by default. Don't just widen the subnet to `192.168.0.0/16`
|
subnet-route space, and blanket-exempting them could route traffic around the tunnel that's actually supposed to go
|
||||||
or `10.0.0.0/8` to compensate: those ranges are also real LAN/VPN
|
through it. If you hit this, either raise `--subnet` to the specific overflow range Docker actually assigned, or
|
||||||
subnet-route space, and blanket-exempting them could route traffic
|
extend the script to enumerate live `docker network inspect` subnets instead of using a fixed range.
|
||||||
around the tunnel that's actually supposed to go through it. If you
|
- Assumes `tailscaled`'s own ip rules stay in the 5210+ priority range observed on the machine this was built on; if a
|
||||||
hit this, either raise `--subnet` to the specific overflow range
|
future Tailscale version changes that, priority 5200 should still safely sit above it as long as it's below whatever
|
||||||
Docker actually assigned, or extend the script to enumerate live
|
Tailscale uses for its catch-all rule.
|
||||||
`docker network inspect` subnets instead of using a fixed range.
|
|
||||||
- Assumes `tailscaled`'s own ip rules stay in the 5210+ priority range
|
|
||||||
observed on the machine this was built on; if a future Tailscale
|
|
||||||
version changes that, priority 5200 should still safely sit above it
|
|
||||||
as long as it's below whatever Tailscale uses for its catch-all rule.
|
|
||||||
|
|||||||
Reference in New Issue
Block a user