support three routing modes as proposed by @bfg100k

Adds `ZEROTIER_ONE_GATEWAY_MODE` variable. Supported values are
`inbound` (forward traffic from ZeroTier cloud to local interfaces),
`outbound` (forward traffic from local interfaces to ZeroTier cloud)
and `both` (bi-directional). Defaults to `inbound`.

Also checks for `net.ipv4.ip_forward=1`. If not enabled, falls back
to standard client mode.

Signed-off-by: Phill Kelley <34226495+Paraphraser@users.noreply.github.com>
This commit is contained in:
Phill Kelley 2022-07-27 21:58:39 +10:00
parent 1ae368044b
commit 09df8bf66e
No known key found for this signature in database
GPG Key ID: 73D35B58592A2E98
3 changed files with 77 additions and 9 deletions

View File

@ -19,6 +19,7 @@ $ docker run --name zerotier-one --device=/dev/net/tun \
--env TZ=Etc/UTC --env PUID=999 -env PGID=994 \ --env TZ=Etc/UTC --env PUID=999 -env PGID=994 \
--env ZEROTIER_ONE_LOCAL_PHYS=eth0 \ --env ZEROTIER_ONE_LOCAL_PHYS=eth0 \
--env ZEROTIER_ONE_USE_IPTABLES_NFT=false \ --env ZEROTIER_ONE_USE_IPTABLES_NFT=false \
--env ZEROTIER_ONE_GATEWAY_MODE=inbound \
--env ZEROTIER_ONE_NETWORK_IDS=«yourDefaultNetworkID(s)» \ --env ZEROTIER_ONE_NETWORK_IDS=«yourDefaultNetworkID(s)» \
-v /var/lib/zerotier-one:/var/lib/zerotier-one zyclonite/zerotier:router -v /var/lib/zerotier-one:/var/lib/zerotier-one zyclonite/zerotier:router
``` ```
@ -55,6 +56,7 @@ services:
- PGID=994 - PGID=994
- ZEROTIER_ONE_LOCAL_PHYS=eth0 - ZEROTIER_ONE_LOCAL_PHYS=eth0
- ZEROTIER_ONE_USE_IPTABLES_NFT=false - ZEROTIER_ONE_USE_IPTABLES_NFT=false
- ZEROTIER_ONE_GATEWAY_MODE=inbound
# - ZEROTIER_ONE_NETWORK_IDS=«yourDefaultNetworkID(s)» # - 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. 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. * `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: This variable is *only* effective on first launch. There is no default if it is omitted. Examples:

View File

@ -19,4 +19,5 @@ services:
- PGID=994 - PGID=994
- ZEROTIER_ONE_LOCAL_PHYS=eth0 - ZEROTIER_ONE_LOCAL_PHYS=eth0
- ZEROTIER_ONE_USE_IPTABLES_NFT=false - ZEROTIER_ONE_USE_IPTABLES_NFT=false
- ZEROTIER_ONE_GATEWAY_MODE=inbound
# - ZEROTIER_ONE_NETWORK_IDS=yourNetworkID # - ZEROTIER_ONE_NETWORK_IDS=yourNetworkID

View File

@ -1,8 +1,6 @@
#!/usr/bin/env sh #!/usr/bin/env sh
set -Eeo pipefail set -Eeo pipefail
echo "$(date) - launching ZeroTier-One in routing mode"
if [ "${1:0:1}" = '-' ]; then if [ "${1:0:1}" = '-' ]; then
set -- zerotier-one "$@" set -- zerotier-one "$@"
fi fi
@ -31,6 +29,19 @@ if [ "$(id -u)" = '0' -a -d "${CONFIG_DIR}" ]; then
chown -Rc "${PUID}:${PGID}" "${CONFIG_DIR}" chown -Rc "${PUID}:${PGID}" "${CONFIG_DIR}"
fi 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 # use an appropriate default for a local physical interface
# (using eth0 maintains backwards compatibility) # (using eth0 maintains backwards compatibility)
PHY_IFACES="${ZEROTIER_ONE_LOCAL_PHYS:-"eth0"}" PHY_IFACES="${ZEROTIER_ONE_LOCAL_PHYS:-"eth0"}"
@ -40,21 +51,50 @@ IPTABLES_CMD=iptables
# but support an override to use iptables-nft # but support an override to use iptables-nft
[ "${ZEROTIER_ONE_USE_IPTABLES_NFT}" = "true" ] && IPTABLES_CMD=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 # the wildcard for the local zerotier interface is
ZT_IFACE="zt+" ZT_IFACE="zt+"
# function to add and remove the requisite rules # function to add and remove the requisite rules
# - $1 is either "A" (add) or "D" (delete) # - $1 is either "A" (add) or "D" (delete)
# - $2 is comment
update_iptables() { update_iptables() {
for PHY_IFACE in ${PHY_IFACES} ; do case "${GATEWAY_MODE}" in
${IPTABLES_CMD} -t nat -${1} POSTROUTING -o ${PHY_IFACE} -j MASQUERADE "inbound" )
${IPTABLES_CMD} -${1} FORWARD -i ${PHY_IFACE} -o ${ZT_IFACE} -m state --state RELATED,ESTABLISHED -j ACCEPT echo "$2 ${IPTABLES_CMD} rules for inbound traffic (ZeroTier to local interfaces ${PHY_IFACES})"
${IPTABLES_CMD} -${1} FORWARD -i ${ZT_IFACE} -o ${PHY_IFACE} -j ACCEPT for PHY_IFACE in ${PHY_IFACES} ; do
done ${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 # 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) # define where the ZeroTier daemon will write its output (if any)
TAIL_PIPE=$(mktemp /tmp/zerotier-ipc-XXXXXX) TAIL_PIPE=$(mktemp /tmp/zerotier-ipc-XXXXXX)
@ -80,7 +120,7 @@ termination_handler() {
echo "$(date) - terminating ZeroTier-One" echo "$(date) - terminating ZeroTier-One"
# remove rules # remove rules
update_iptables "D" update_iptables "D" "removing"
# relay the termination message to the daemon # relay the termination message to the daemon
if [ -d "/proc/${ZEROTIER_DAEMON_PID}" ] ; then if [ -d "/proc/${ZEROTIER_DAEMON_PID}" ] ; then