Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow switching gateway modes without recreating/restarting the container #30

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/router.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
sudo apt-get install -y qemu-user-static

- name: Build unstable image
if: ${{ inputs.event == 'push' }}
if: ${{ inputs.event == 'push' || inputs.event == 'pull_request' }}
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
Expand Down Expand Up @@ -103,7 +103,7 @@ jobs:
buildah manifest add --arch s390x ${{ env.IMAGE_NAME }}:latest ${{ env.IMAGE_NAME }}:s390x

- name: Push unstable images
if: ${{ inputs.event == 'push' }}
if: ${{ inputs.event == 'push' || inputs.event == 'pull_request' }}
run: |
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
Expand Down
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
175 changes: 175 additions & 0 deletions scripts/gatewaymode
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#!/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 "I" (insert), "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 "Command:"
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). Default if no command specified."
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 ${mode}
;;
* )
echo "Warning: Gateway mode (${1}) is not supported - ignored"
return 1
;;
esac
return 0
}

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

Loading