diff --git a/README-router.md b/README-router.md index b215618..0fe1c83 100644 --- a/README-router.md +++ b/README-router.md @@ -19,6 +19,7 @@ $ docker run --name zerotier-one --device=/dev/net/tun \ --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 ``` @@ -55,6 +56,7 @@ services: - PGID=994 - ZEROTIER_ONE_LOCAL_PHYS=eth0 - ZEROTIER_ONE_USE_IPTABLES_NFT=false + - ZEROTIER_ONE_GATEWAY_MODE=inbound # - ZEROTIER_ONE_NETWORK_IDS=«yourDefaultNetworkID(s)» ``` @@ -130,6 +132,31 @@ Note: 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: diff --git a/docker-compose-router.yml b/docker-compose-router.yml index 35dfcfa..c09319e 100644 --- a/docker-compose-router.yml +++ b/docker-compose-router.yml @@ -19,4 +19,5 @@ services: - 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/scripts/entrypoint-router.sh b/scripts/entrypoint-router.sh index f991a3b..8672be8 100755 --- a/scripts/entrypoint-router.sh +++ b/scripts/entrypoint-router.sh @@ -1,8 +1,6 @@ #!/usr/bin/env sh set -Eeo pipefail -echo "$(date) - launching ZeroTier-One in routing mode" - if [ "${1:0:1}" = '-' ]; then set -- zerotier-one "$@" fi @@ -31,6 +29,19 @@ 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"}" @@ -40,21 +51,50 @@ 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() { - 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 + 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" +update_iptables "A" "adding" # define where the ZeroTier daemon will write its output (if any) TAIL_PIPE=$(mktemp /tmp/zerotier-ipc-XXXXXX) @@ -80,7 +120,7 @@ termination_handler() { echo "$(date) - terminating ZeroTier-One" # remove rules - update_iptables "D" + update_iptables "D" "removing" # relay the termination message to the daemon if [ -d "/proc/${ZEROTIER_DAEMON_PID}" ] ; then