diff --git a/.github/workflows/multiarch.yml b/.github/workflows/multiarch.yml index 59bd0b9..af8515c 100644 --- a/.github/workflows/multiarch.yml +++ b/.github/workflows/multiarch.yml @@ -103,8 +103,8 @@ jobs: buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:latest buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:${{ github.event.release.tag_name }} - bridge: - uses: ./.github/workflows/bridge.yml + router: + uses: ./.github/workflows/router.yml needs: push with: tag: ${{ github.event.release.tag_name }} diff --git a/.github/workflows/bridge.yml b/.github/workflows/router.yml similarity index 87% rename from .github/workflows/bridge.yml rename to .github/workflows/router.yml index 4f34a8c..2292607 100644 --- a/.github/workflows/bridge.yml +++ b/.github/workflows/router.yml @@ -1,4 +1,4 @@ -name: Bridge Mode build +name: Router Mode build on: workflow_call: inputs: @@ -43,11 +43,11 @@ jobs: - name: Build unstable image if: ${{ inputs.event == 'push' }} - run: buildah bud --platform ${{ matrix.platform.name }} --build-arg FROM_IMAGE=${{ format('ghcr.io/zyclonite/{0}', env.IMAGE_NAME) }} --build-arg FROM_VERSION=main -f ./Dockerfile.bridge -t ${{ env.IMAGE_NAME }}:${{ matrix.platform.tag }} . + run: buildah bud --platform ${{ matrix.platform.name }} --build-arg FROM_IMAGE=${{ format('ghcr.io/zyclonite/{0}', env.IMAGE_NAME) }} --build-arg FROM_VERSION=main -f ./Dockerfile.router -t ${{ env.IMAGE_NAME }}:${{ matrix.platform.tag }} . - name: Build stable image if: ${{ inputs.event == 'release' }} - run: buildah bud --platform ${{ matrix.platform.name }} --build-arg FROM_IMAGE=${{ format('ghcr.io/zyclonite/{0}', env.IMAGE_NAME) }} --build-arg FROM_VERSION=${{ inputs.tag }} -f ./Dockerfile.bridge -t ${{ env.IMAGE_NAME }}:${{ matrix.platform.tag }} . + run: buildah bud --platform ${{ matrix.platform.name }} --build-arg FROM_IMAGE=${{ format('ghcr.io/zyclonite/{0}', env.IMAGE_NAME) }} --build-arg FROM_VERSION=${{ inputs.tag }} -f ./Dockerfile.router -t ${{ env.IMAGE_NAME }}:${{ matrix.platform.tag }} . - name: Check images created run: buildah images | grep '${{ env.IMAGE_NAME }}' @@ -98,16 +98,16 @@ jobs: - name: Push unstable images if: ${{ inputs.event == 'push' }} run: | - buildah manifest push --all --format v2s2 --creds zyclonite:${{ secrets.REGISTRY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://docker.io/zyclonite/${{ env.IMAGE_NAME }}:bridge-main - buildah manifest push --all --creds zyclonite:${{ secrets.QUAY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://quay.io/zyclonite/${{ env.IMAGE_NAME }}:bridge-main - buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:bridge-main + buildah manifest push --all --format v2s2 --creds zyclonite:${{ secrets.REGISTRY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://docker.io/zyclonite/${{ env.IMAGE_NAME }}:router-main + buildah manifest push --all --creds zyclonite:${{ secrets.QUAY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://quay.io/zyclonite/${{ env.IMAGE_NAME }}:router-main + buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:router-main - name: Push stable images if: ${{ inputs.event == 'release' }} run: | - buildah manifest push --all --format v2s2 --creds zyclonite:${{ secrets.REGISTRY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://docker.io/zyclonite/${{ env.IMAGE_NAME }}:bridge - buildah manifest push --all --format v2s2 --creds zyclonite:${{ secrets.REGISTRY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://docker.io/zyclonite/${{ env.IMAGE_NAME }}:bridge-${{ github.event.release.tag_name }} - buildah manifest push --all --creds zyclonite:${{ secrets.QUAY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://quay.io/zyclonite/${{ env.IMAGE_NAME }}:bridge - buildah manifest push --all --creds zyclonite:${{ secrets.QUAY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://quay.io/zyclonite/${{ env.IMAGE_NAME }}:bridge-${{ github.event.release.tag_name }} - buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:bridge - buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:bridge-${{ github.event.release.tag_name }} + buildah manifest push --all --format v2s2 --creds zyclonite:${{ secrets.REGISTRY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://docker.io/zyclonite/${{ env.IMAGE_NAME }}:router + buildah manifest push --all --format v2s2 --creds zyclonite:${{ secrets.REGISTRY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://docker.io/zyclonite/${{ env.IMAGE_NAME }}:router-${{ github.event.release.tag_name }} + buildah manifest push --all --creds zyclonite:${{ secrets.QUAY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://quay.io/zyclonite/${{ env.IMAGE_NAME }}:router + buildah manifest push --all --creds zyclonite:${{ secrets.QUAY_PASSWORD }} ${{ env.IMAGE_NAME }}:latest docker://quay.io/zyclonite/${{ env.IMAGE_NAME }}:router-${{ github.event.release.tag_name }} + buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:router + buildah manifest push --all --creds zyclonite:${{ secrets.GITHUB_TOKEN }} ${{ env.IMAGE_NAME }}:latest docker://ghcr.io/zyclonite/${{ env.IMAGE_NAME }}:router-${{ github.event.release.tag_name }} diff --git a/Dockerfile.bridge b/Dockerfile.router similarity index 55% rename from Dockerfile.bridge rename to Dockerfile.router index b7126b6..2d3b3e6 100644 --- a/Dockerfile.bridge +++ b/Dockerfile.router @@ -4,20 +4,20 @@ ARG FROM_VERSION=latest FROM ${FROM_IMAGE}:${FROM_VERSION} LABEL org.opencontainers.image.title="zerotier" \ - org.opencontainers.image.version="bridge-${ZT_VERSION}" \ - org.opencontainers.image.description="ZeroTier One as Docker Image" \ + org.opencontainers.image.version="router-${ZT_VERSION}" \ + org.opencontainers.image.description="ZeroTier One router as Docker Image" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.source="https://github.com/zyclonite/zerotier-docker" ENV LOG_PATH=/var/log/supervisor -COPY scripts/entrypoint-bridge.sh /usr/sbin/ +COPY scripts/entrypoint-router.sh /usr/sbin/ -RUN apk add --no-cache --purge --clean-protected iptables \ +RUN apk add --no-cache --purge --clean-protected iptables tzdata \ && rm -rf /var/cache/apk/* EXPOSE 9993/udp -ENTRYPOINT ["entrypoint-bridge.sh"] +ENTRYPOINT ["entrypoint-router.sh"] CMD ["-U"] diff --git a/README-router.md b/README-router.md new file mode 100644 index 0000000..0fe1c83 --- /dev/null +++ b/README-router.md @@ -0,0 +1,199 @@ +## zerotier router + +### Description + +This is a variation built on top of the zyclonite/zerotier container which implements a local network router. It is based upon the ZeroTier Knowledge Base article: + +* [Route between ZeroTier and Physical Networks](https://zerotier.atlassian.net/wiki/spaces/SD/pages/224395274/Route+between+ZeroTier+and+Physical+Networks) + +Technically, this could be described as a *half-router*: + +* You can initiate connections *from* a remote client *to* devices on the LAN; but +* You can't initiate connections *to* the remote client *from* devices on the LAN. + +### Command line example + +``` console +$ docker run --name zerotier-one --device=/dev/net/tun \ + --cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add=SYS_ADMIN \ + --env TZ=Etc/UTC --env PUID=999 -env PGID=994 \ + --env ZEROTIER_ONE_LOCAL_PHYS=eth0 \ + --env ZEROTIER_ONE_USE_IPTABLES_NFT=false \ + --env ZEROTIER_ONE_GATEWAY_MODE=inbound \ + --env ZEROTIER_ONE_NETWORK_IDS=«yourDefaultNetworkID(s)» \ + -v /var/lib/zerotier-one:/var/lib/zerotier-one zyclonite/zerotier:router +``` + +Note: + +* Environment variables that can contain multiple values should be enclosed in quotes with the components separated by spaces. Example: + + ``` console + --env ZEROTIER_ONE_LOCAL_PHYS="eth0 wlan0" + ``` + +### Compose file example + +``` yaml +version: '3' +services: + zerotier: + image: "zyclonite/zerotier:router" + container_name: zerotier-one + devices: + - /dev/net/tun + network_mode: host + volumes: + - '/var/lib/zerotier-one:/var/lib/zerotier-one' + cap_add: + - NET_ADMIN + - SYS_ADMIN + - NET_RAW + restart: unless-stopped + environment: + - TZ=Etc/UTC + - PUID=999 + - PGID=994 + - ZEROTIER_ONE_LOCAL_PHYS=eth0 + - ZEROTIER_ONE_USE_IPTABLES_NFT=false + - ZEROTIER_ONE_GATEWAY_MODE=inbound + # - ZEROTIER_ONE_NETWORK_IDS=«yourDefaultNetworkID(s)» +``` + +Note: + +* The right hand sides of environment variables should *never* be enclosed in quotes. If you need to pass multiple values, separate them with spaces. Example: + + ``` yaml + environment: + - ZEROTIER_ONE_LOCAL_PHYS=eth0 wlan0 + ``` + +### Environment variables + +* `TZ` – timezone support. Example: + + ``` yaml + environment: + - TZ=Australia/Sydney + ``` + + Defaults to `Etc/UTC` if omitted. + +* `PUID` + `PGID` – user and group IDs for ownership of persistent store. Example: + + ``` yaml + environment: + - PUID=1000 + - PGID=1000 + ``` + + If omitted, `PUID` defaults to user ID 999, while `PGID` defaults to group ID 994. + + These variables are only used to ensure consistent ownership of persistent storage on each launch. They do not affect how the container *runs.* Absent a `user:` directive, the container runs as root and does not downgrade its privileges. + +* `ZEROTIER_ONE_LOCAL_PHYS` - a space-separated list of physical interfaces that should be configured to participate in NAT-based routing. Examples: + + - Use only the physical Ethernet interface (this is also the default of the variable is omitted): + + ``` yaml + environment: + - ZEROTIER_ONE_LOCAL_PHYS=eth0 + ``` + + - If your computer only has WiFi active (eg Raspberry Pi Zero W2): + + ``` yaml + environment: + - ZEROTIER_ONE_LOCAL_PHYS=wlan0 + ``` + + - If your computer has both Ethernet and WiFi interfaces active and you wish to be able to route through each interface: + + ``` yaml + environment: + - ZEROTIER_ONE_LOCAL_PHYS=eth0 wlan0 + ``` + + This scheme could be appropriate where the physical interfaces were: + + 1. In the same broadcast domain (subnet). Disconnecting Ethernet would fail-over to WiFi. + 2. In different broadcast domains, such as if you allocated different subnets for Ethernet and WiFi. + +* `ZEROTIER_ONE_USE_IPTABLES_NFT` - controls the command the container uses to set up NAT forwarding. Example: + + ``` yaml + environment: + - ZEROTIER_ONE_USE_IPTABLES_NFT=true + ``` + + - `false` means the container uses `iptables`. This is the default. + - `true` means the container uses `iptables-nft`. + + Try `true` if NAT does not seem to be working. This is needed on Raspberry Pi Bullseye. + +* `ZEROTIER_ONE_GATEWAY_MODE` - controls the traffic direction. Examples: + + - Only permit traffic *from* the ZeroTier cloud *to* the local physical interfaces: + + ``` yaml + environment: + - ZEROTIER_ONE_GATEWAY_MODE=inbound + ``` + + - Only permit traffic *from* the local physical interfaces *to* the ZeroTier cloud: + + ``` yaml + environment: + - ZEROTIER_ONE_GATEWAY_MODE=outbound + ``` + + - Permit bi-directional traffic between the local physical interfaces and the ZeroTier cloud: + + ``` yaml + environment: + - ZEROTIER_ONE_GATEWAY_MODE=both + ``` + + Defaults to `inbound` if omitted. Note that you will probably need one or more static routes configured in your local LAN router so that traffic originating in a local host which is not running the ZeroTier client can be directed to the gateway host. + +* `ZEROTIER_ONE_NETWORK_IDS` – a space-separated list of ZeroTier network IDs. + + This variable is *only* effective on first launch. There is no default if it is omitted. Examples: + + - to join a single network: + + ``` yaml + environment: + - ZEROTIER_ONE_NETWORK_IDS=aaaaaaaaaaaaaaaa + ``` + + Equivalent of running the following command after the container first starts: + + ``` + $ docker exec zerotier zerotier-cli join aaaaaaaaaaaaaaaa + ``` + + - to join a multiple networks: + + ``` yaml + environment: + - ZEROTIER_ONE_NETWORK_IDS=aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbb + ``` + + Equivalent of running the following commands after the container first starts: + + ``` + $ docker exec zerotier zerotier-cli join aaaaaaaaaaaaaaaa + $ docker exec zerotier zerotier-cli join bbbbbbbbbbbbbbbb + ``` + + It does not matter whether you use this environment variable or the `join` command, you still need to use ZeroTier Central to approve the computer for each network it joins. + +### Managed route(s) + +For each ZeroTier container that is configured as a router, ZeroTier needs at least one *Managed Route*. + +The [ZeroTier Wiki](https://zerotier.atlassian.net/wiki/spaces/SD/pages/224395274/Route+between+ZeroTier+and+Physical+Networks#Configure-the-ZeroTier-managed-route) explains how to design managed routes. + +You configure Managed Routes in ZeroTier Central. diff --git a/README.md b/README.md index 1d92209..06d8641 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,11 @@ or create an empty file with the network as name /var/lib/zerotier-one/networks.d/8056c2e21c000001.conf -#### Bridge mode -It is the implementation of the local network bridge [paper](https://zerotier.atlassian.net/wiki/spaces/SD/pages/193134593/Bridge+your+ZeroTier+and+local+network+with+a+RaspberryPi) +#### Router mode - docker run --name zerotier-one --device=/dev/net/tun \ - --cap-add=NET_ADMIN --cap-add=NET_RAW --cap-add=SYS_ADMIN \ - -v /var/lib/zerotier-one:/var/lib/zerotier-one zyclonite/zerotier:bridge +A variation on the container which implements a local network router. See: -That will start the zero-one, establish connection and build the bridge once the `zt` interface is up. +* [router README](./README-router.md) #### Source diff --git a/docker-compose-router.yml b/docker-compose-router.yml new file mode 100644 index 0000000..c09319e --- /dev/null +++ b/docker-compose-router.yml @@ -0,0 +1,23 @@ +version: '3' +services: + zerotier: + image: "zyclonite/zerotier:router" + container_name: zerotier-one + devices: + - /dev/net/tun + network_mode: host + volumes: + - '/var/lib/zerotier-one:/var/lib/zerotier-one' + cap_add: + - NET_ADMIN + - SYS_ADMIN + - NET_RAW + restart: unless-stopped + environment: + - TZ=Etc/UTC + - PUID=999 + - PGID=994 + - ZEROTIER_ONE_LOCAL_PHYS=eth0 + - ZEROTIER_ONE_USE_IPTABLES_NFT=false + - ZEROTIER_ONE_GATEWAY_MODE=inbound + # - ZEROTIER_ONE_NETWORK_IDS=yourNetworkID diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index 6e53ca0..f15edbb 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -25,7 +25,7 @@ spec: containers: - name: zerotier - image: zyclonite/zerotier:bridge + image: zyclonite/zerotier:router resources: limits: memory: "128Mi" diff --git a/scripts/entrypoint-bridge.sh b/scripts/entrypoint-bridge.sh deleted file mode 100755 index 1d89214..0000000 --- a/scripts/entrypoint-bridge.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env sh -set -Eeo pipefail - -if [ "${1:0:1}" = '-' ]; then - set -- zerotier-one "$@" -fi - -PHY_IFACE=eth0 -ZT_IFACE="zt+" -iptables -t nat -A POSTROUTING -o $PHY_IFACE -j MASQUERADE -iptables -A FORWARD -i $PHY_IFACE -o $ZT_IFACE -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -A FORWARD -i $ZT_IFACE -o $PHY_IFACE -j ACCEPT - -exec "$@" diff --git a/scripts/entrypoint-router.sh b/scripts/entrypoint-router.sh new file mode 100755 index 0000000..8672be8 --- /dev/null +++ b/scripts/entrypoint-router.sh @@ -0,0 +1,154 @@ +#!/usr/bin/env sh +set -Eeo pipefail + +if [ "${1:0:1}" = '-' ]; then + set -- zerotier-one "$@" +fi + +# useful paths +CONFIG_DIR="/var/lib/zerotier-one" +NETWORKS_DIR="${CONFIG_DIR}/networks.d" + +# set up network auto-join if (a) the networks directory does not exist +# and (b) the ZEROTIER_ONE_NETWORK_IDS environment variable is non-null. +if [ ! -d "${NETWORKS_DIR}" -a -n "${ZEROTIER_ONE_NETWORK_IDS}" ] ; then + echo "Assuming container first run." + mkdir -p "${NETWORKS_DIR}" + for NETWORK_ID in ${ZEROTIER_ONE_NETWORK_IDS} ; do + echo "Configuring auto-join of network ID: ${NETWORK_ID}" + touch "${NETWORKS_DIR}/${NETWORK_ID}.conf" + echo "You will need to authorize this host at:" + echo " https://my.zerotier.com/network/${NETWORK_ID}" + done +fi + +# make sure permissions are always as expected (self-repair) +PUID="${PUID:-"999"}" +PGID="${PGID:-"994"}" +if [ "$(id -u)" = '0' -a -d "${CONFIG_DIR}" ]; then + chown -Rc "${PUID}:${PGID}" "${CONFIG_DIR}" +fi + +# is routing enabled? +if [ $(sysctl -n net.ipv4.ip_forward) -ne 1 ] ; then + + # no! there is no point in setting up rules or termination handler + echo "$(date) - IPv4 forwarding not enabled - launching ZeroTier-One in non-routing mode" + + # just exec the client (this script ends here) + exec "$@" + +fi + +echo "$(date) - launching ZeroTier-One in routing mode" + +# use an appropriate default for a local physical interface +# (using eth0 maintains backwards compatibility) +PHY_IFACES="${ZEROTIER_ONE_LOCAL_PHYS:-"eth0"}" + +# default to iptables (maintains backwards compatibility) +IPTABLES_CMD=iptables +# but support an override to use iptables-nft +[ "${ZEROTIER_ONE_USE_IPTABLES_NFT}" = "true" ] && IPTABLES_CMD=iptables-nft + +# the default forwarding mode is inbound (backwards compatible) +GATEWAY_MODE="${ZEROTIER_ONE_GATEWAY_MODE:-"inbound"}" + +# the wildcard for the local zerotier interface is +ZT_IFACE="zt+" + +# function to add and remove the requisite rules +# - $1 is either "A" (add) or "D" (delete) +# - $2 is comment +update_iptables() { + case "${GATEWAY_MODE}" in + "inbound" ) + echo "$2 ${IPTABLES_CMD} rules for inbound traffic (ZeroTier to local interfaces ${PHY_IFACES})" + for PHY_IFACE in ${PHY_IFACES} ; do + ${IPTABLES_CMD} -t nat -${1} POSTROUTING -o ${PHY_IFACE} -j MASQUERADE + ${IPTABLES_CMD} -${1} FORWARD -i ${PHY_IFACE} -o ${ZT_IFACE} -m state --state RELATED,ESTABLISHED -j ACCEPT + ${IPTABLES_CMD} -${1} FORWARD -i ${ZT_IFACE} -o ${PHY_IFACE} -j ACCEPT + done + ;; + "outbound" ) + echo "$2 ${IPTABLES_CMD} rules for outbound traffic (local interfaces ${PHY_IFACES} to ZeroTier)" + ${IPTABLES_CMD} -t nat -${1} POSTROUTING -o ${ZT_IFACE} -j MASQUERADE + for PHY_IFACE in ${PHY_IFACES} ; do + ${IPTABLES_CMD} -${1} FORWARD -i ${ZT_IFACE} -o ${PHY_IFACE} -m state --state RELATED,ESTABLISHED -j ACCEPT + ${IPTABLES_CMD} -${1} FORWARD -i ${PHY_IFACE} -o ${ZT_IFACE} -j ACCEPT + done + ;; + "both" ) + echo "$2 ${IPTABLES_CMD} rules for bi-directional traffic (local interfaces ${PHY_IFACES} to/from ZeroTier)" + ${IPTABLES_CMD} -t nat -${1} POSTROUTING -o ${ZT_IFACE} -j MASQUERADE + for PHY_IFACE in ${PHY_IFACES} ; do + ${IPTABLES_CMD} -t nat -${1} POSTROUTING -o ${PHY_IFACE} -j MASQUERADE + ${IPTABLES_CMD} -${1} FORWARD -i ${ZT_IFACE} -o ${PHY_IFACE} -j ACCEPT + ${IPTABLES_CMD} -${1} FORWARD -i ${PHY_IFACE} -o ${ZT_IFACE} -j ACCEPT + done + ;; + * ) + echo "Warning: ZEROTIER_ONE_GATEWAY_MODE=${GATEWAY_MODE} is not supported - ignored" + ;; + esac +} + +# add rules to set up NAT-routing +update_iptables "A" "adding" + +# define where the ZeroTier daemon will write its output (if any) +TAIL_PIPE=$(mktemp /tmp/zerotier-ipc-XXXXXX) + +# start listening and echoing anything that appears there into this process +tail -f "${TAIL_PIPE}" & + +# make a note of the process ID for tail +TAIL_PIPE_PID=${!} + +# start the ZeroTier daemon in detached state +nohup "$@" "${TAIL_PIPE}" 2>&1 & + +# make a note of the process ID +ZEROTIER_DAEMON_PID=${!} + +# report +echo "$(date) - ZeroTier daemon is running as process ${ZEROTIER_DAEMON_PID}" + +# function to handle cleanup +termination_handler() { + + echo "$(date) - terminating ZeroTier-One" + + # remove rules + update_iptables "D" "removing" + + # relay the termination message to the daemon + if [ -d "/proc/${ZEROTIER_DAEMON_PID}" ] ; then + kill -TERM ${ZEROTIER_DAEMON_PID} + wait ${ZEROTIER_DAEMON_PID} + fi + + # tell the pipe listener to go away too + if [ -d "/proc/${TAIL_PIPE_PID}" ] ; then + kill -TERM ${TAIL_PIPE_PID} + wait ${TAIL_PIPE_PID} + fi + + # clean up the pipe file + rm "${TAIL_PIPE}" + +} + +# set up termination handler (usually catches TERM) +trap termination_handler INT TERM HUP + +# suspend this script while the zerotier daemon is running +wait ${ZEROTIER_DAEMON_PID} + +# would not usually expect to arrive here inside a Docker container but +# it can happen if the user does a "sudo killall zerotier-one" rather +# that use Docker commands +echo "$(date) - the ZeroTier daemon has quit unexpectedly - cleaning up" + +# run the termination handler +termination_handler