diff --git a/Dockerfile b/Dockerfile index 3ceaefe..076c074 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ ARG ALPINE_VERSION=3.21 ARG ZT_COMMIT=185a3a2c76e6bf1b1c0415871f43076638eb007c ARG ZT_VERSION=1.14.2 -FROM ${ALPINE_IMAGE}:${ALPINE_VERSION} as builder +FROM ${ALPINE_IMAGE}:${ALPINE_VERSION} AS builder ARG ZT_COMMIT @@ -27,7 +27,9 @@ LABEL org.opencontainers.image.title="zerotier" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.source="https://github.com/zyclonite/zerotier-docker" -COPY --from=builder /src/zerotier-one /scripts/entrypoint.sh /usr/sbin/ +COPY --from=builder /src/zerotier-one /scripts/entrypoint.sh /scripts/healthcheck.sh /usr/sbin/ + +RUN chmod +x /usr/sbin/healthcheck.sh RUN apk add --no-cache --purge --clean-protected libc6-compat libstdc++ \ && mkdir -p /var/lib/zerotier-one \ @@ -40,3 +42,6 @@ EXPOSE 9993/udp ENTRYPOINT ["entrypoint.sh"] CMD ["-U"] + +HEALTHCHECK --interval=60s --timeout=8s --retries=2 --start-period=60s --start-interval=3s \ + CMD ["/bin/sh", "/usr/sbin/healthcheck.sh"] diff --git a/README.md b/README.md index 06d8641..c64da07 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,39 @@ or create an empty file with the network as name /var/lib/zerotier-one/networks.d/8056c2e21c000001.conf +#### Auto-Join Networks + +Define: + +``` +ZT_NETWORK_IDS= [Network IDs with Space in between each ID] +``` + + +The container will JOIN ALL the Networks mentioned in this variable and LEAVE ANY OTHER Network NOT defined in this variable. + + +#### Health Checks + +Environment Variable Options: + + 1. Check For Specific Networks: + + ``` + ZT_CHK_SPECIFIC_NETWORKS=[Enter Specific Networks for checking with a space between each network; ALL Networks mentioned here would be checked; ZT_CHK_MIN_ROUTES_FOR_HEALTH is ignored if this is used.] + ``` + + 2. Check for Minimum number of Connections: + + ``` + ZT_CHK_MIN_ROUTES_FOR_HEALTH=[Should be a Number greater than 0; Ignored if CHK_ZT_SPECIFIC_NETWORK is used.] + ``` + + 3. Check for ALL Networks to be connected: + This is default mode when no Environment variable is defined. + + + #### Router mode A variation on the container which implements a local network router. See: diff --git a/docker-compose-router.yml b/docker-compose-router.yml index c09319e..b3041ac 100644 --- a/docker-compose-router.yml +++ b/docker-compose-router.yml @@ -20,4 +20,8 @@ services: - ZEROTIER_ONE_LOCAL_PHYS=eth0 - ZEROTIER_ONE_USE_IPTABLES_NFT=false - ZEROTIER_ONE_GATEWAY_MODE=inbound - # - ZEROTIER_ONE_NETWORK_IDS=yourNetworkID + #Variables For AutoJoin + #- ZT_NETWORK_IDS + #Variables for Health-Check + #- ZT_CHK_SPECIFIC_NETWORKS + #- ZT_CHK_MIN_ROUTES_FOR_HEALTH \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 87292a7..f9298ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -11,3 +11,9 @@ services: cap_add: - NET_ADMIN - SYS_ADMIN + #environment: + #Variables For AutoJoin + #- ZT_NETWORK_IDS + #Variables for Health-Check + #- ZT_CHK_SPECIFIC_NETWORKS + #- ZT_CHK_MIN_ROUTES_FOR_HEALTH \ No newline at end of file diff --git a/scripts/entrypoint-router.sh b/scripts/entrypoint-router.sh index 825dfec..a1374fa 100755 --- a/scripts/entrypoint-router.sh +++ b/scripts/entrypoint-router.sh @@ -22,6 +22,40 @@ if [ ! -d "${NETWORKS_DIR}" -a -n "${ZEROTIER_ONE_NETWORK_IDS}" ] ; then done fi +#Match Networks with User Provided Networks +# Check if ZT_NETWORK_IDS is not NULL +if [[ -n "$ZT_NETWORK_IDS" ]] ; then + #First leave networks not a part of user provided networks + current_joined_networks=$(zerotier-cli listnetworks | awk 'NR>1 {print$3}') + for current_network in $current_joined_networks; do + found_network=0 + for zt_network in $ZT_NETWORK_IDS; do + if [[ "$current_network" = "$zt_network" ]]; then + found_network=1 + break 1; + fi + done + if [[ $found_network -eq 0 ]]; then + zerotier-cli leave $current_network + fi + done + #Join Networks not present + #Resetting joined Networks for optimisation + current_joined_networks=$(zerotier-cli listnetworks | awk 'NR>1 {print$3}') + for zt_network in $ZT_NETWORK_IDS; do + found_network=0 + for current_network in $current_joined_networks; do + if [[ "$current_network" = "$zt_network" ]]; then + found_network=1 + break 1; + fi + done + if [[ $found_network -eq 0 ]]; then + zerotier-cli join $zt_network + fi + done +fi + # make sure permissions are always as expected (self-repair) PUID="${PUID:-"999"}" PGID="${PGID:-"994"}" diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index df5bbf1..8086991 100755 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -9,6 +9,40 @@ DEFAULT_PRIMARY_PORT=9993 DEFAULT_PORT_MAPPING_ENABLED=true DEFAULT_ALLOW_TCP_FALLBACK_RELAY=true +#Match Networks with User Provided Networks +# Check if ZT_NETWORK_IDS is not NULL +if [[ -n "$ZT_NETWORK_IDS" ]] ; then + #First leave networks not a part of user provided networks + current_joined_networks=$(zerotier-cli listnetworks | awk 'NR>1 {print$3}') + for current_network in $current_joined_networks; do + found_network=0 + for zt_network in $ZT_NETWORK_IDS; do + if [[ "$current_network" = "$zt_network" ]]; then + found_network=1 + break 1; + fi + done + if [[ $found_network -eq 0 ]]; then + zerotier-cli leave $current_network + fi + done + #Join Networks not present + #Resetting joined Networks for optimisation + current_joined_networks=$(zerotier-cli listnetworks | awk 'NR>1 {print$3}') + for zt_network in $ZT_NETWORK_IDS; do + found_network=0 + for current_network in $current_joined_networks; do + if [[ "$current_network" = "$zt_network" ]]; then + found_network=1 + break 1; + fi + done + if [[ $found_network -eq 0 ]]; then + zerotier-cli join $zt_network + fi + done +fi + MANAGEMENT_NETWORKS="" if [ ! -z "$ZT_ALLOW_MANAGEMENT_FROM" ]; then for NETWORK in ${ZT_ALLOW_MANAGEMENT_FROM//,/$IFS}; do diff --git a/scripts/healthcheck.sh b/scripts/healthcheck.sh new file mode 100644 index 0000000..4d280b3 --- /dev/null +++ b/scripts/healthcheck.sh @@ -0,0 +1,105 @@ +#!/bin/sh +#MIT License +# +#Copyright (c) PMGA Tech LLP +# +#Contributers: +# PMGA Tech LLP +# gb-123-git +# +#Permission is hereby granted, free of charge, to any person obtaining a copy +#of this software and associated documentation files (the "Software"), to deal +#in the Software without restriction, including without limitation the rights +#to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +#copies of the Software, and to permit persons to whom the Software is +#furnished to do so, subject to the following conditions: +# +#The above copyright notice and this permission notice alongwith the following +#paragraph shall be included in all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +#IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +#FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +#AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +#LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +#OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +#SOFTWARE. + +#Exit Codes +# 0= Success +# 1= Failure + +#Environment Variables +# ZT_CHK_SPECIFIC_NETWORKS= +# ZT_CHK_MIN_ROUTES_FOR_HEALTH= + +# Check if specified Networks are all Connected +if [[ -n "${ZT_CHK_SPECIFIC_NETWORKS}" ]] ; then + for network in $ZT_CHK_SPECIFIC_NETWORKS; do + #If Network is OK, continue, else exit + if [[ "$(zerotier-cli get ${network} status)" = "OK" ]]; then + #Check for Routes + interface=$(zerotier-cli get ${network} portDeviceName) + routes=$(ip r | grep "dev ${interface}" | grep -cv "via") + #Exit if No routes + if [[ ${routes} -lt 1 ]]; then + exit 1 + else + continue + #echo "${network} Connected." + fi + #Network Status Not = OK + else + exit 1 + fi + done + exit 0 +# Check for Minimum Networks +elif [[ -n "${ZT_CHK_MIN_ROUTES_FOR_HEALTH}" ]] ; then + # Validate the MIN value for Health Checks + ZT_CHK_MIN_ROUTES_FOR_HEALTH=$(( ${ZT_CHK_MIN_ROUTES_FOR_HEALTH} < 1 ? 1 : ${ZT_CHK_MIN_ROUTES_FOR_HEALTH} )) + #echo "No. Of Networks to Check: ${ZT_CHK_MIN_ROUTES_FOR_HEALTH}" + network_count=0 + #Get List of Joined networks + joined_networks=$(zerotier-cli listnetworks | awk 'NR>1 {print$3}') + for network in $joined_networks; do + if [[ "$(zerotier-cli get ${network} status)" = "OK" ]] ; then + #Check for Routes + interface=$(zerotier-cli get ${network} portDeviceName) + routes=$(ip r | grep "dev ${interface}" | grep -cv "via") + if [[ ${routes} -lt 1 ]]; then + exit 1 + else + network_count=$(expr $network_count + 1) + #echo "${network} Connected." + fi + if [[ ${network_count} -ge ${ZT_CHK_MIN_ROUTES_FOR_HEALTH} ]] ; then + #echo "${network_count} Networks Connected. Exit Success" + exit 0 + fi + fi + done + #Exit if the above count was not reached + exit 1 +#Check if ALL Networks are connected (Default - ZeroTier) +else + #echo "Checking All Networks" + joined_networks=$(zerotier-cli listnetworks | awk 'NR>1 {print$3}') + #If there are no Networks, exit Failure + [[ -n "${joined_networks}" ]] || exit 1 + for network in $joined_networks; do + #[[ "$(zerotier-cli get ${network} status)" = "OK" ]] || exit 1 + if [[ "$(zerotier-cli get ${network} status)" = "OK" ]]; then + #Check for Routes + interface=$(zerotier-cli get ${network} portDeviceName) + routes=$(ip r | grep "dev ${interface}" | grep -cv "via") + #Exit if No routes + if [[ ${routes} -lt 1 ]]; then + exit 1 + else + continue + #echo "${network} Connected." + fi + fi + done +fi