WIP: Ship the tools the VPN toggle grants capability for #28
@@ -307,6 +307,18 @@ container is created once by a very long function where a dropped capability is
|
|||||||
container and make the switch impossible to turn off.
|
container and make the switch impossible to turn off.
|
||||||
- Off is byte-identical to a container created before the feature existed, and a missing label
|
- Off is byte-identical to a container created before the feature existed, and a missing label
|
||||||
reads as `false`, so no existing project is churned.
|
reads as `false`, so no existing project is churned.
|
||||||
|
- **The toggle grants capability and stops there — it routes nothing.** `vpn_host_config()` returns
|
||||||
|
a cap, a device and a sysctl; no client is installed, no route is touched, no tunnel is started
|
||||||
|
or restored. Users read the name as "turn the VPN on" and report the default network not routing
|
||||||
|
through it as a bug. It isn't, and the docs say so explicitly; keep it that way.
|
||||||
|
- **The tooling is baked, not installed at runtime.** `iproute2` and `wireguard-tools` are in
|
||||||
|
`container/Dockerfile` because a runtime install lands in the writable layer and is lost on
|
||||||
|
base-image migration — leaving a project holding the capability with nothing able to exercise it,
|
||||||
|
and no error that points at why. `iptables` is deliberately absent; see the Dockerfile comment.
|
||||||
|
- **Anything built on this fails open.** The network namespace is rebuilt on every start and no
|
||||||
|
service manager runs inside, so a tunnel never survives stop/start or recreation while `/run`
|
||||||
|
state persists through the snapshot and makes it look as though it did. Traffic silently reverts
|
||||||
|
to the real address. Any future autostart or killswitch work starts here.
|
||||||
|
|
||||||
### Container Lifecycle
|
### Container Lifecycle
|
||||||
|
|
||||||
|
|||||||
+22
-1
@@ -475,7 +475,13 @@ When enabled, the host Docker socket is mounted into the container so Claude Cod
|
|||||||
|
|
||||||
When enabled, the container is given the three things a VPN client needs to build a tunnel:
|
When enabled, the container is given the three things a VPN client needs to build a tunnel:
|
||||||
the `NET_ADMIN` capability, the `/dev/net/tun` device, and the `net.ipv4.conf.all.src_valid_mark`
|
the `NET_ADMIN` capability, the `/dev/net/tun` device, and the `net.ipv4.conf.all.src_valid_mark`
|
||||||
sysctl that WireGuard requires. This is **off by default**.
|
sysctl that WireGuard requires. The `ip` and `wg` commands are always present to use them. This is
|
||||||
|
**off by default**.
|
||||||
|
|
||||||
|
**This setting makes a tunnel possible; it does not make one.** Nothing is connected, no traffic is
|
||||||
|
redirected, and no client is installed or started on your behalf. Enabling it and expecting the
|
||||||
|
container's traffic to start leaving through a VPN is the most common misreading of what it does —
|
||||||
|
installing a client and routing traffic into it remains yours to do.
|
||||||
|
|
||||||
Without it, a client such as PIA, WireGuard or OpenVPN installs and its daemon starts normally, but
|
Without it, a client such as PIA, WireGuard or OpenVPN installs and its daemon starts normally, but
|
||||||
the connection attempt **hangs until it times out** — a default container has no tun device to open
|
the connection attempt **hangs until it times out** — a default container has no tun device to open
|
||||||
@@ -497,6 +503,21 @@ Things worth knowing:
|
|||||||
**start**, with an error naming `/dev/net/tun` and pointing back at this setting.
|
**start**, with an error naming `/dev/net/tun` and pointing back at this setting.
|
||||||
- A VPN client's kill switch applies to everything in the container, Claude Code included. If the
|
- A VPN client's kill switch applies to everything in the container, Claude Code included. If the
|
||||||
tunnel drops, expect API calls to fail until it reconnects or the kill switch is turned off.
|
tunnel drops, expect API calls to fail until it reconnects or the kill switch is turned off.
|
||||||
|
- **No tunnel survives a restart.** The network namespace is built fresh every time the container
|
||||||
|
starts, and there is no service manager inside to reconnect anything. Files under `/run` may
|
||||||
|
persist via the snapshot and make it *look* like the tunnel is still configured, but after any
|
||||||
|
stop/start, Reset or recreation the interface and its routes are gone and traffic goes out your
|
||||||
|
real address again — with no error and nothing visibly different. Re-establish it after every
|
||||||
|
start, and check rather than assume.
|
||||||
|
- **A full tunnel breaks DNS unless the client is told to leave private ranges alone.** Containers
|
||||||
|
resolve through an address on the Docker network (`192.168.65.7` under Docker Desktop) that sits
|
||||||
|
outside the container's own subnet, so a default route of `0.0.0.0/0` — or a `0.0.0.0/1` plus
|
||||||
|
`128.0.0.0/1` pair — captures it and sends every lookup into a tunnel that cannot carry it. The
|
||||||
|
symptom is total: Claude Code reports it cannot connect, because it cannot resolve
|
||||||
|
`api.anthropic.com`. Route `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` and `169.254.0.0/16`
|
||||||
|
via the original gateway, and use the VPN provider's own resolver for everything else. Note that
|
||||||
|
a health check which fetches an IP literal such as `1.1.1.1` passes cleanly while this is broken —
|
||||||
|
resolve a name instead.
|
||||||
|
|
||||||
> This setting can only be changed when the container is stopped. Capabilities and devices are
|
> This setting can only be changed when the container is stopped. Capabilities and devices are
|
||||||
> fixed when a container is created, so toggling it recreates the container on the next start.
|
> fixed when a container is created, so toggling it recreates the container on the next start.
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ RUN for i in 1 2 3 4 5; do \
|
|||||||
cron \
|
cron \
|
||||||
bubblewrap \
|
bubblewrap \
|
||||||
socat \
|
socat \
|
||||||
|
iproute2 \
|
||||||
|
wireguard-tools \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# `libnss3-tools` above provides `certutil`. Chrome/Chromium read neither
|
# `libnss3-tools` above provides `certutil`. Chrome/Chromium read neither
|
||||||
@@ -42,6 +44,23 @@ RUN for i in 1 2 3 4 5; do \
|
|||||||
# corporate CA, no matter what the system trust store says. entrypoint.sh
|
# corporate CA, no matter what the system trust store says. entrypoint.sh
|
||||||
# degrades to a warning if it is ever missing.
|
# degrades to a warning if it is ever missing.
|
||||||
|
|
||||||
|
# `iproute2` and `wireguard-tools` above are what the VPN support toggle
|
||||||
|
# (`vpn_support_enabled`) grants capability *for*. That toggle hands a project
|
||||||
|
# CAP_NET_ADMIN and /dev/net/tun; without `ip` there is then no way to add a
|
||||||
|
# route, and without `wg` no way to build the tunnel those two exist to serve —
|
||||||
|
# a capability with nothing able to use it.
|
||||||
|
#
|
||||||
|
# They are baked rather than left to a runtime `apt-get install` for the same
|
||||||
|
# reason as the Playwright libraries below: the writable layer is re-paid after
|
||||||
|
# every Reset and lost on base-image migration. A hand-installed `wg` therefore
|
||||||
|
# works right up until an upgrade, then disappears and takes the tunnel with it
|
||||||
|
# — silently, since a VPN that fails to come up looks exactly like one that was
|
||||||
|
# never started. Together they are ~4.3 MB including dependencies.
|
||||||
|
#
|
||||||
|
# `iptables` is deliberately NOT here. The only thing that wants it is a desktop
|
||||||
|
# VPN client's killswitch, and those clients need a GUI that a container has no
|
||||||
|
# way to give them; leaving it out keeps the reach of CAP_NET_ADMIN smaller.
|
||||||
|
|
||||||
# Remove default ubuntu user to free UID 1000 for host-user remapping
|
# Remove default ubuntu user to free UID 1000 for host-user remapping
|
||||||
RUN if id ubuntu >/dev/null 2>&1; then userdel -r ubuntu 2>/dev/null || userdel ubuntu; fi \
|
RUN if id ubuntu >/dev/null 2>&1; then userdel -r ubuntu 2>/dev/null || userdel ubuntu; fi \
|
||||||
&& if getent group ubuntu >/dev/null 2>&1; then groupdel ubuntu 2>/dev/null || true; fi
|
&& if getent group ubuntu >/dev/null 2>&1; then groupdel ubuntu 2>/dev/null || true; fi
|
||||||
|
|||||||
Reference in New Issue
Block a user