diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 109324b..e5e61c0 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -9,3 +9,12 @@ updates: - dependencies - github-actions + - package-ecosystem: docker + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 10 + labels: + - dependencies + - docker + diff --git a/README.md b/README.md index a8a7bc3..d99a7d9 100644 --- a/README.md +++ b/README.md @@ -20,21 +20,21 @@ You also need to add a static route into ZeroTier so that the traffic is routed ### Docker Compose -**You need to edit the `ZT_NETWORKS` and `ARCH` variable in the `docker-compose.yml` file first to add your networks and make sure your acrhitecture is correct (see [this page](http://download.zerotier.com/debian/buster/pool/main/z/zerotier-one/) for examples, usually either amd64 or arm64)** +Edit the `ZT_NETWORKS` variable in `docker-compose.yml` to add your networks. Multi-arch images are published automatically; no architecture changes are needed. Easiest way to bring up is via Docker Compose. Rename `docker-compose.yml.example` to `docker-compose.yml` and run `docker compose up -d`. -If you want to disable bridging, set `ZT_BRIDGE=false`. This can be done after the initial networks have been joined (just change the environment variable in the `docker-compose.yml` file and run `), as the ZeroTier config persists but IPTables forwarding is done on each container startup. +If you want to disable bridging, set `ZT_BRIDGE=false`. This can be done after the initial networks have been joined (just change the environment variable in the `docker-compose.yml` file and restart), as the ZeroTier config persists but IPTables forwarding is done on each container startup. ### OG Docker `docker build -t zerotierbridge .` -`docker run --privileged -e ZT_NETWORKS=NETWORK_1 NETWORK_2 -e ZT_BRIDGE=true zerotierbridge:latest` +`docker run --cap-add NET_ADMIN --cap-add NET_RAW --sysctl net.ipv4.ip_forward=1 -e ZT_NETWORKS="NETWORK_1 NETWORK_2" -e ZT_BRIDGE=true zerotierbridge:latest` Add your network ID(s) into the `ZT_NETWORKS` argument, space separated. -Disable bridging by passing `ZT_BRIDGE=false`. This can be done after the initial networks have been joined (just rebuild the container), as the ZeroTier config persists but IPTables forwarding is done on each container startup. +Disable bridging by passing `ZT_BRIDGE=false`. This can be done after the initial networks have been joined (just restart the container), as the ZeroTier config persists but IPTables forwarding is done on each container startup. #### Persistent Storage @@ -42,8 +42,6 @@ If you would like the container to retain the same ZeroTier client ID on reboot, `docker run --privileged -e ZT_NETWORKS=NETWORK_ID_HERE ZT_BRIDGE=true -v zt_config:/var/lib/zerotier-one/ zerotierbridge:latest` -#### Caveat: Architecture +#### Notes -If you need to run this on a device with different architecture (a raspberry pi, for instance), then just edit line 3 of the Dockerfile. - -If you were using a Raspberry Pi 4, you would change this to `ARCH=arm64` and the container will pull the correct ZeroTier installer. +If your host requires additional privileges for networking, you may need to add device and capabilities in your runtime configuration. The provided Docker Compose example includes `cap_add: [NET_ADMIN, NET_RAW]` and `sysctls` for IP forwarding. diff --git a/docker-compose.yml.example b/docker-compose.yml.example index 543f251..caf4df5 100644 --- a/docker-compose.yml.example +++ b/docker-compose.yml.example @@ -5,11 +5,15 @@ services: container_name: zerotierbridge image: registry.dangerous.tech/dangeroustech/zerotierbridge restart: always - privileged: true + cap_add: + - NET_ADMIN + - NET_RAW + sysctls: + net.ipv4.ip_forward: "1" volumes: - zt_config:/var/lib/zerotier-one environment: - - ZT_NETWORKS=NETWORK_ID_1 NETWORK_ID_2 NETWORK_ID_3 - - ZT_BRIDGE=true + ZT_NETWORKS: "NETWORK_ID_1 NETWORK_ID_2 NETWORK_ID_3" + ZT_BRIDGE: "true" volumes: zt_config: \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 7135efb..3f5db97 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,15 +1,22 @@ #!/bin/sh -grepzt() { - (find /proc -name exe | xargs -I{} readlink {}) 2>/dev/null | grep -q zerotier-one - return $? +set -eu + +terminate() { + # Try to terminate zerotier-one gracefully + if [ -n "${ZT_PID:-}" ]; then + kill -TERM "$ZT_PID" 2>/dev/null || true + wait "$ZT_PID" 2>/dev/null || true + fi } +trap terminate INT TERM echo "starting zerotier" setsid /usr/sbin/zerotier-one & +ZT_PID=$! -while ! grepzt -do +# Wait for zerotier to be responsive +until zerotier-cli info >/dev/null 2>&1; do echo "zerotier hasn't started, waiting a second" sleep 1 done @@ -17,34 +24,34 @@ done # Set IPTables to allow NATting sysctl -w net.ipv4.ip_forward=1 > /dev/null -echo "joining networks: $ZT_NETWORKS" +echo "joining networks: ${ZT_NETWORKS:-}" -for n in $ZT_NETWORKS -do +for n in ${ZT_NETWORKS:-}; do echo "joining $n" - while ! zerotier-cli join "$n" - do + until zerotier-cli join "$n"; do echo "joining $n failed; trying again in 1s" sleep 1 done - if [ "$ZT_BRIDGE" = "true" ] - then - echo "Configuring iptables on $(zerotier-cli get $n portDeviceName)" - PHY_IFACE=eth0; ZT_IFACE=$(zerotier-cli get $n portDeviceName) + if [ "${ZT_BRIDGE:-true}" = "true" ]; then + ZT_IFACE=$(zerotier-cli get "$n" portDeviceName) + PHY_IFACE=eth0 + echo "Configuring iptables on ${ZT_IFACE}" - iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE - iptables -t nat -A POSTROUTING -o $ZT_IFACE -j MASQUERADE - iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -j ACCEPT - iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT + # idempotent rules + iptables -t nat -C POSTROUTING -o "$PHY_IFACE" -j MASQUERADE 2>/dev/null || iptables -t nat -A POSTROUTING -o "$PHY_IFACE" -j MASQUERADE + iptables -t nat -C POSTROUTING -o "$ZT_IFACE" -j MASQUERADE 2>/dev/null || iptables -t nat -A POSTROUTING -o "$ZT_IFACE" -j MASQUERADE + iptables -C FORWARD -i "$PHY_IFACE" -o "$ZT_IFACE" -j ACCEPT 2>/dev/null || iptables -A FORWARD -i "$PHY_IFACE" -o "$ZT_IFACE" -j ACCEPT + iptables -C FORWARD -i "$ZT_IFACE" -o "$PHY_IFACE" -j ACCEPT 2>/dev/null || iptables -A FORWARD -i "$ZT_IFACE" -o "$PHY_IFACE" -j ACCEPT fi done -# Give ZT a second realise it's online +# Give ZT a second to realise it's online sleep 10 # Print Client Info -echo "$(zerotier-cli info)" +zerotier-cli info || true -sleep infinity \ No newline at end of file +# Keep the container running while zerotier-one is alive +wait "$ZT_PID" \ No newline at end of file