Skip to content

Commit

Permalink
allow switching gateway modes without recreating/restarting the conta…
Browse files Browse the repository at this point in the history
…iner
  • Loading branch information
bfg100k committed Feb 22, 2024
1 parent d18dfe3 commit ef5ea76
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Dockerfile.router
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LABEL org.opencontainers.image.title="zerotier" \

ENV LOG_PATH=/var/log/supervisor

COPY scripts/entrypoint-router.sh /usr/sbin/
COPY scripts/entrypoint-router.sh scripts/gatewaymode /usr/sbin/

RUN apk add --no-cache --purge --clean-protected iptables iptables-legacy tzdata \
&& rm -rf /var/cache/apk/*
Expand Down
54 changes: 2 additions & 52 deletions scripts/entrypoint-router.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,61 +42,11 @@ 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-legacy
# 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
${IPTABLES_CMD} -${1} FORWARD -i ${PHY_IFACE} -o ${ZT_IFACE} -j DROP
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
${IPTABLES_CMD} -${1} FORWARD -i ${ZT_IFACE} -o ${PHY_IFACE} -j DROP
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"
gatewaymode "${GATEWAY_MODE}"

# define where the ZeroTier daemon will write its output (if any)
TAIL_PIPE="/tmp/zerotier-ipc-log"
Expand All @@ -123,7 +73,7 @@ termination_handler() {
echo "$(date) - terminating ZeroTier-One"

# remove rules
update_iptables "D" "removing"
gatewaymode disable

# relay the termination message to the daemon
# (the pipe listener is cleaned up automatically)
Expand Down
176 changes: 176 additions & 0 deletions scripts/gatewaymode
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
#!/bin/sh

# 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-legacy
# but support an override to use iptables-nft
[ "${ZEROTIER_ONE_USE_IPTABLES_NFT}" = "true" ] && IPTABLES_CMD=iptables-nft

# 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 requested mode
_update_iptables() {
local action
case "${1}" in
"I" )
action="Inserting"
;;
"A" )
action="Adding"
;;
"D" )
action="Deleting"
;;
esac

case "${2}" in
"inbound" )
echo "${action} ${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} -j DROP
${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 "${action} ${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} -j DROP
${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 "${action} ${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
;;
"none" )
echo "${action} ${IPTABLES_CMD} rules for _no_ traffic (local interfaces ${PHY_IFACES} to/from ZeroTier)"
for PHY_IFACE in ${PHY_IFACES} ; do
${IPTABLES_CMD} -${1} FORWARD -i ${ZT_IFACE} -o ${PHY_IFACE} -j DROP
${IPTABLES_CMD} -${1} FORWARD -i ${PHY_IFACE} -o ${ZT_IFACE} -j DROP
done
esac
}

# Get current gateway mode by looking up what iptable rules are in place
_get_current_mode() {

if [ -n "$( ${IPTABLES_CMD} -S -t nat 2> /dev/null | grep "\-o ${ZT_IFACE}" )" ]; then
#either outbound or both
if [ -n "$( ${IPTABLES_CMD} -S | grep "\-i ${ZT_IFACE}.*RELATED" )" ]; then
echo "outbound"
else
echo "both"
fi
elif [ -n "$( ${IPTABLES_CMD} -S | grep "\-i ${ZT_IFACE}.*ACCEPT" )" ]; then
echo "inbound"
elif [ -n "$( ${IPTABLES_CMD} -S | grep "\-i ${ZT_IFACE}.*DROP" )" ]; then
echo "none"
else
echo "disabled"
fi
}


_usage() {
echo "Usage: $0 inbound | outbound | both | none | disable | status"
echo "Set, query or disable gateway mode."
echo ""
echo "Commands:"
echo " inbound Only permit traffic from the ZeroTier cloud to the local physical interfaces."
echo " outbound Only permit traffic from the local physical interfaces to the ZeroTier cloud."
echo " both Permit bi-directional traffic between the local physical interfaces and the ZeroTier cloud."
echo " none Block all traffic between the local physical interfaces and the ZeroTier cloud."
echo " disable Remove iptable rules. NOTE: because default forward rule is accept, this behaves like \"both\"."
echo " status Show current gateway mode (e.g. inbound, outbound, etc)"
echo ""
exit $1
}

main() {
local i

local opt
local mode=$( _get_current_mode )
case "${1}" in
"inbound" )
if [ ${mode} == "inbound" ]; then
echo "Already in mode inbound."
break
fi
if [ ! ${mode} == "disabled" ]; then
_update_iptables D ${mode}
fi
_update_iptables I inbound
;;
"outbound" )
if [ ${mode} == "outbound" ] ; then
echo "Already in mode outbound."
break
fi
if [ ! ${mode} == "disabled" ]; then
_update_iptables D ${mode}
fi
_update_iptables I outbound
;;
"both" )
if [ ${mode} == "both" ]; then
echo "Already in mode both."
break
fi
if [ ! ${mode} == "disabled" ]; then
_update_iptables D ${mode}
fi
_update_iptables I both
;;
"none" )
if [ ${mode} == "none" ]; then
echo "Already in mode none."
break
fi
if [ ! ${mode} == "disabled" ]; then
_update_iptables D ${mode}
fi
_update_iptables I none
;;
"disable" )
if [ ${mode} == "disabled" ]; then
echo "Already disabled."
break
fi
if [ ! ${mode} == "disabled" ]; then
_update_iptables D ${mode}
fi
;;
"status" )
echo ${mode}
;;
"" )
echo "Please specify a valid argument."
_usage 0
;;
* )
echo "Warning: Gateway mode (${1}) is not supported - ignored"
return 1
;;
esac
return 0
}

main "${@}"
exit ${?}

0 comments on commit ef5ea76

Please sign in to comment.