#!/bin/sh # TAILMON (TAILMON.SH) is an all-in-one script that is optimized to install, maintain and monitor a Tailscale service and # connection from your Asus-Merlin FW router. It provides the basic steps needed to install and implement a successful # connection to your tailnet. It allows for 2 different modes of operation: Kernel and Userspace modes. Depending on your # needs, you can also enable exit node and subnet route advertisements. Separately, TAILMON functions as a Tailscale # monitor application that will sit in the background (using the -screen utility), and will restart the Tailscale service # should it happen to go down. Many thanks to: @jksmurf, @ColinTaylor, @Aiadi, and @kuki68ster for all their help, input # and testing of this script! # Last Updated: 2025-Aug-24 #Preferred standard router binaries path export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change version="1.3.0" beta=0 # Beta indicator on/off track=0 # Stable/Beta Track subscription apppath="/jffs/scripts/tailmon.sh" # Static path to the app config="/jffs/addons/tailmon.d/tailmon.cfg" # Static path to the config file dlverpath="/jffs/addons/tailmon.d/version.txt" # Static path to the version file bverpath="/jffs/addons/tailmon.d/beta.txt" # Static path to the beta version file logfile="/jffs/addons/tailmon.d/tailmon.log" # Static path to the log tmemails="/jffs/addons/tailmon.d/tmemails.txt" # Static path to email rate limit file routerboot=0 # Tracking router reboot notifications tsinstalled=0 keepalive=0 timerloop=60 logsize=2000 autostart=0 schedule=0 # Scheduler enable y/n schedulehrs=1 # Scheduler hours schedulemin=0 # Scheduler mins updatetm=0 # Autoupdate TAILMON Script updatets=0 # Autoupdate Tailscale Binaries amtmemailsuccess=0 amtmemailfailure=0 ratelimit=0 # Rate limiting number of emails/houre exitnode=0 advroutes=1 accroutes=0 persistentsettings=0 tsoperatingmode="Userspace" precmd="" args="--tun=userspace-networking --state=/opt/var/tailscaled.state --statedir=/opt/var/lib/tailscale" preargs="nohup" routes="$(nvram get lan_ipaddr | cut -d"." -f1-3).0/24" customcmdline="" #AMTM Email Notification Variables readonly scriptFileName="${0##*/}" readonly scriptFileNTag="${scriptFileName%.*}" readonly CEM_LIB_TAG="master" readonly CEM_LIB_URL="https://raw.githubusercontent.com/Martinski4GitHub/CustomMiscUtils/${CEM_LIB_TAG}/EMail" readonly CUSTOM_EMAIL_LIBDir="/jffs/addons/shared-libs" readonly CUSTOM_EMAIL_LIBName="CustomEMailFunctions.lib.sh" readonly CUSTOM_EMAIL_LIBFile="${CUSTOM_EMAIL_LIBDir}/$CUSTOM_EMAIL_LIBName" # Color variables CBlack="\e[1;30m" InvBlack="\e[1;40m" CRed="\e[1;31m" InvRed="\e[1;41m" CGreen="\e[1;32m" InvGreen="\e[1;42m" CDkGray="\e[1;90m" InvDkGray="\e[1;100m" InvLtGray="\e[1;47m" CYellow="\e[1;33m" InvYellow="\e[1;43m" CBlue="\e[1;34m" InvBlue="\e[1;44m" CMagenta="\e[1;35m" CCyan="\e[1;36m" InvCyan="\e[1;46m" CWhite="\e[1;37m" InvWhite="\e[1;107m" CClear="\e[0m" # ------------------------------------------------------------------------------------------------------------------------- # FUNCTIONS BEGIN # ------------------------------------------------------------------------------------------------------------------------- # ------------------------------------------------------------------------------------------------------------------------- # LogoNM is a function that displays the BACKUPMON script name in a cool ASCII font without menu options logoNM () { clear echo "" echo "" echo "" echo -e "${CDkGray} _________ ______ __ _______ _ __" echo -e " /_ __/ | / _/ / / |/ / __ \/ | / /" echo -e " / / / /| | / // / / /|_/ / / / / |/ /" echo -e " / / / ___ |_/ // /___/ / / / /_/ / /| /" echo -e " /_/ /_/ |_/___/_____/_/ /_/\____/_/ |_/ v$version" echo "" echo "" printf "\r ${CGreen} [ INITIALIZING ] ${CClear}" sleep 1 clear echo "" echo "" echo "" echo -e "${CYellow} _________ ______ __ _______ _ __" echo -e " /_ __/ | / _/ / / |/ / __ \/ | / /" echo -e " / / / /| | / // / / /|_/ / / / / |/ /" echo -e " / / / ___ |_/ // /___/ / / / /_/ / /| /" echo -e " /_/ /_/ |_/___/_____/_/ /_/\____/_/ |_/ v$version" echo "" echo "" printf "\r ${CGreen}[ INITIALIZING ... DONE ]${CClear}" sleep 1 printf "\r ${CGreen} [ LOADING... ] ${CClear}" sleep 1 } logoNMexit () { clear echo "" echo "" echo "" echo -e "${CYellow} _________ ______ __ _______ _ __" echo -e " /_ __/ | / _/ / / |/ / __ \/ | / /" echo -e " / / / /| | / // / / /|_/ / / / / |/ /" echo -e " / / / ___ |_/ // /___/ / / / /_/ / /| /" echo -e " /_/ /_/ |_/___/_____/_/ /_/\____/_/ |_/ v$version" echo "" echo "" printf "\r ${CGreen} [ SHUTTING DOWN ] ${CClear}" sleep 1 clear echo "" echo "" echo "" echo -e "${CDkGray} _________ ______ __ _______ _ __" echo -e " /_ __/ | / _/ / / |/ / __ \/ | / /" echo -e " / / / /| | / // / / /|_/ / / / / |/ /" echo -e " / / / ___ |_/ // /___/ / / / /_/ / /| /" echo -e " /_/ /_/ |_/___/_____/_/ /_/\____/_/ |_/ v$version" echo "" echo "" printf "\r ${CGreen} [ SHUTTING DOWN ] ${CClear}" sleep 1 printf "\r ${CDkGray} [ GOODBYE... ] ${CClear}\n\n" sleep 1 } # ------------------------------------------------------------------------------------------------------------------------- # Promptyn is a simple function that accepts y/n input promptyn() { # No defaults, just y or n while true; do read -p "$1" -n 1 -r yn case "${yn}" in [Yy]* ) return 0 ;; [Nn]* ) return 1 ;; * ) echo -e "\nPlease answer y or n.";; esac done } # ------------------------------------------------------------------------------------------------------------------------- # Spinner is a script that provides a small indicator on the screen to show script activity spinner() { spins=$1 spin=0 totalspins=$((spins / 4)) while [ $spin -le $totalspins ]; do for spinchar in / - \\ \|; do printf "\r$spinchar" sleep 1 done spin=$((spin+1)) done printf "\r" } # ------------------------------------------------------------------------------------------------------------------------- # Preparebar and Progressbar is a script that provides a nice progressbar to show script activity preparebar() { barlen=$1 barspaces=$(printf "%*s" "$1") barchars=$(printf "%*s" "$1" | tr ' ' "$2") } progressbaroverride() { insertspc=" " bypasswancheck=0 if [ $1 -eq -1 ]; then printf "\r $barspaces\r" else if [ ! -z $7 ] && [ $1 -ge $7 ]; then barch=$(($7*barlen/$2)) barsp=$((barlen-barch)) progr=$((100*$1/$2)) else barch=$(($1*barlen/$2)) barsp=$((barlen-barch)) progr=$((100*$1/$2)) fi if [ ! -z $6 ]; then AltNum=$6; else AltNum=$1; fi if [ "$5" == "Standard" ]; then printf " ${CWhite}${InvDkGray}$AltNum${4} / ${progr}%%${CClear} [${CGreen}e${CClear}=Exit] [Selection? ${InvGreen} ${CClear}${CGreen}]\r${CClear}" "$barchars" "$barspaces" fi fi # Borrowed this wonderful keypress capturing mechanism from @Eibgrad... thank you! :) key_press=''; read -rsn1 -t 1 key_press < "$(tty 0>&2)" if [ $key_press ]; then case $key_press in [Aa]) vconfig;; [Cc]) vsetup;; [Dd]) tsdown;; [Ee]) logoNMexit; echo -e "${CClear}\n"; exit 0;; [Kk]) vconfig;; [Ll]) vlogs;; [Mm]) timerloopconfig;; [Oo]) if [ "$tsoperatingmode" == "Custom" ]; then customconfig; fi;; [Rr]) restarttsc;; [Ss]) startts;; [Tt]) stopts;; [Uu]) tsup;; *) timer=$timerloop;; esac fi } progressbarpause() { insertspc=" " bypasswancheck=0 if [ "$1" -eq -1 ] then printf "\r $barspaces\r" else if [ $# -gt 6 ] && [ -n "$7" ] && [ "$1" -ge "$7" ] then barch="$(($7*barlen/$2))" barsp="$((barlen-barch))" progr="$((100*$1/$2))" else barch="$(($1*barlen/$2))" barsp="$((barlen-barch))" progr="$((100*$1/$2))" fi if [ $# -gt 5 ] && [ -n "$6" ]; then AltNum="$6" ; else AltNum="$1" ; fi if [ "$5" = "Standard" ] then printf " ${CWhite}${InvDkGray}Continuing in $AltNum/5...${CClear} [${CGreen}s${CClear}=Setup] [${CGreen}e${CClear}=Exit] [Selection? ${InvGreen} ${CClear}${CGreen}]\r${CClear}" "$barchars" "$barspaces" fi fi # Borrowed this wonderful keypress capturing mechanism from @Eibgrad... thank you! :) key_press=''; read -rsn1 -t 1 key_press < "$(tty 0>&2)" if [ $key_press ] then case $key_press in [Ss]) vsetup;; [Ee]) logoNMexit; echo -e "${CClear}\n"; exit 0;; esac fi } # ------------------------------------------------------------------------------------------------------------------------- # Initial setup menu initialsetup() { clear echo -e "${InvGreen} ${InvDkGray}${CWhite} TAILMON Initial Setup ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} TAILMON has not been configured yet, and Tailscale will need to be installed and${CClear}" echo -e "${InvGreen} ${CClear} configured. You can choose between 'Express Install' and 'Advanced Install'.${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} 1) Express Install will automatically download and install Tailscale, choosing the${CClear}" echo -e "${InvGreen} ${CClear} 'Userspace' mode of operation and configures it to advertise routes of your local${CClear}" echo -e "${InvGreen} ${CClear} subnet by default. A URL prompt will appear which will require you to copy this link" echo -e "${InvGreen} ${CClear} into your browser to connect this device to your tailnet." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} 2) Advanced Install will launch the TAILMON Setup/Configuration Menu, and allows${CClear}" echo -e "${InvGreen} ${CClear} you to manually choose your preferred settings, such as 'Kernel' vs. 'Userspace'${CClear}" echo -e "${InvGreen} ${CClear} mode, and letting you pick the exit node option along with additional subnets." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Before starting, please familiarize yourself with how Tailscale works. Please use${CClear}" echo -e "${InvGreen} ${CClear} @ColinTaylor's Wiki available here:${CClear}" echo -e "${InvGreen} ${CClear} https://github.com/RMerl/asuswrt-merlin.ng/wiki/Installing-Tailscale-through-Entware${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} It is also advised to have an account set and ready to go on https://tailscale.com${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" read -p "Please select? (1=Express Install, 2=Advanced Install, e=Exit): " SelectSetup case $SelectSetup in 1) echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON Express Install initiated." >> $logfile expressinstall;; 2) echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON Advanced Install initiated." >> $logfile exec sh /jffs/scripts/tailmon.sh -setup;; [Ee]) echo -e "${CClear}"; echo ""; exit 0;; esac } # ------------------------------------------------------------------------------------------------------------------------- # Expressinstall script expressinstall() { echo "" echo -e "Ready to Express Install Tailscale?" if promptyn "[y/n]: " then if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. echo "" echo -e "\n${CGreen}Updating Entware Packages...${CClear}" echo "" opkg update echo "" echo -e "Installing Entware ${CGreen}CoreUtils-Timeout${CClear} Package...${CClear}" echo "" opkg install coreutils-timeout echo "" echo -e "Installing Entware ${CGreen}Screen${CClear} Package...${CClear}" echo "" opkg install screen echo "" echo -e "${CGreen}Installing Tailscale Package(s)...${CClear}" echo "" archker=$(opkg print-architecture | grep "armv7-2.6") if [ -z "$archker" ]; then opkg install tailscale else opkg install tailscale_nohf #install special tailscale package for arm7 kernel 2.6 fi echo "" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Entware package installed." >> $logfile else clear echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" echo -e "Please install Entware using the AMTM utility before proceeding..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Entware was not found installed on router. Please investigate." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key exit 1 fi else echo "" echo -e "${CClear}" exit 0 fi echo "" echo -e "${CGreen}Applying settings for Userspace mode of operation...${CClear}" tsoperatingmode="Userspace" precmd="" args="--tun=userspace-networking --state=/opt/var/tailscaled.state --statedir=/opt/var/lib/tailscale" preargs="nohup" saveconfig echo "" echo -e "${CGreen}Applying settings to Tailscale service and connection...${CClear}" if [ -f "/opt/bin/tailscale" ]; then #make mods to the S06tailscaled service for Userspace mode if [ "$tsoperatingmode" == "Userspace" ]; then sed -i "s/^ARGS=.*/ARGS=\"--tun=userspace-networking\ --state=\/opt\/var\/tailscaled.state\ --statedir=\/opt\/var\/lib\/tailscale\"/" "/opt/etc/init.d/S06tailscaled" sed -i "s/^PREARGS=.*/PREARGS=\"nohup\"/" "/opt/etc/init.d/S06tailscaled" sed -i -e '/^PRECMD=/d' "/opt/etc/init.d/S06tailscaled" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Userspace Mode settings have been applied." >> $logfile #remove firewall-start entry if found if [ -f /jffs/scripts/firewall-start ]; then if grep -q -F "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi" /jffs/scripts/firewall-start; then sed -i -e '/tailscale down/d' /jffs/scripts/firewall-start fi fi fi else echo "" echo -e "${CRed}ERROR: Tailscale binary was not found. Please check Entware and router/drive for errors.${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Tailscale binaries not found on router. Please investigate." >> $logfile exit 1 fi echo "" echo -e "${CGreen}Starting Tailscale service...${CClear}" echo "" /opt/etc/init.d/S06tailscaled start echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Service started." >> $logfile echo "" echo "" echo -e "${CGreen}Starting Tailscale connection...${CClear}" echo "" echo -e "${CGreen}Please be prepared to copy and paste the link below into your browser, and connect this device" echo -e "to your tailnet (Tailscale Network)${CClear}" echo "" advroutescmd="--advertise-routes=$routes" echo -e "${CGreen}Executing: tailscale up $advroutescmd${CClear}" echo "" tailscale up $advroutescmd echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Connection started." >> $logfile echo "" echo "" echo -e "${CGreen}Express Install Completed Successfully!${CClear}" echo "" read -rsp $'Press any key to continue...\n' -n1 key exec sh /jffs/scripts/tailmon.sh -noswitch echo -e "${CClear}" exit 0 } # ------------------------------------------------------------------------------------------------------------------------- # Install script installts() { clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Install Tailscale ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} This installer will download and install Tailscale from the Entware respository." echo -e "${InvGreen} ${CClear} It will also check for any prerequisites before install commences." echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "Install Tailscale?" if promptyn "[y/n]: " then if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. echo "" echo -e "\n${CGreen}Updating Entware Packages...${CClear}" echo "" opkg update echo "" echo -e "${CGreen}Installing Tailscale Package(s)...${CClear}" echo "" archker=$(opkg print-architecture | grep "armv7-2.6") if [ -z "$archker" ]; then opkg install tailscale else opkg install tailscale_nohf #install special tailscale package for arm7 kernel 2.6 fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Entware package installed." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key else clear echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" echo -e "Please install Entware using the AMTM utility before proceeding..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Entware was not found on router. Please investigate." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key exit 1 fi fi resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # Uninstall script uninstallts() { clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Uninstall Tailscale ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} This uninstaller utility will remove Tailscale Entware packages from your router" echo -e "${InvGreen} ${CClear} along with all files and modifications made to Tailscale." echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "Uninstall Tailscale?" if promptyn "[y/n]: " then if [ -f /opt/bin/tailscale ]; then if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. echo "" echo -e "\n${CGreen}Shutting down Tailscale...${CClear}" tailscale logout tailscale down echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Connection shut down and logged out." >> $logfile /opt/etc/init.d/S06tailscaled stop echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Service shut down." >> $logfile echo "" echo -e "\n${CGreen}Removing firewall-start entries...${CClear}" #remove firewall-start entry if found if [ -f /jffs/scripts/firewall-start ]; then if grep -q -F "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi" /jffs/scripts/firewall-start; then sed -i -e '/tailscale down/d' /jffs/scripts/firewall-start echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: firewall-start entries removed." >> $logfile fi fi echo "" echo -e "\n${CGreen}Updating Entware Packages...${CClear}" echo "" opkg update echo "" echo -e "${CGreen}Uninstalling Entware Tailscale Package(s)...${CClear}" echo "" archker=$(opkg print-architecture | grep "armv7-2.6") if [ -z "$archker" ]; then opkg remove tailscale else opkg remove tailscale_nohf #remove special tailscale package for arm7 kernel 2.6 fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Entware package removed." >> $logfile # Removed the various folders tailscale could hide rm -f /opt/var/tailscaled.state >/dev/null 2>&1 rm -r /opt/var/lib/tailscale >/dev/null 2>&1 rm -r /opt/var/run/tailscale >/dev/null 2>&1 rm -r /var/run/tailscale >/dev/null 2>&1 rm -r /var/lib/tailscale >/dev/null 2>&1 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale files and folders removed." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key else clear echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" echo -e "Please install Entware using the AMTM utility before proceeding..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Entware not found on router. Please investigate." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key exit 1 fi else echo "" echo -e "\n${CGreen}Tailscale was not found installed on this router.${CClear}" echo "" read -rsp $'Press any key to continue...\n' -n1 key fi fi resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # start service script startts() { printf "\33[2K\r" printf "${CGreen}\r[Starting Tailscale Service]" sleep 1 printf "\33[2K\r" echo -e "${CGreen}Messages:" echo "" /opt/etc/init.d/S06tailscaled start tsstat=$? if [ "$tsstat" -ne 0 ]; then echo "" echo -e "${CRed}ERROR: Tailscale Service did not start correctly${CClear}" echo "" #Display a standard timer# timer=0 while [ $timer -ne 5 ] do timer="$((timer+1))" preparebar 46 "|" progressbarpause $timer 5 "" "s" "Standard" done printf "\33[2K\r" fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Service started." >> $logfile echo "" resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # stop service script stopts() { printf "\33[2K\r" printf "${CGreen}\r[Stopping Tailscale Service]" sleep 1 printf "\33[2K\r" echo -e "${CGreen}Messages:" echo "" /opt/etc/init.d/S06tailscaled stop echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Service stopped." >> $logfile echo "" resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # restart service and connection restarttsc() { printf "\33[2K\r" printf "${CGreen}\r[Restarting Tailscale Service/Connection]${CClear}" sleep 1 tsdown stopts #make mods to the S06tailscaled service for Userspace mode if [ "$tsoperatingmode" == "Userspace" ]; then applyuserspacemode #make mods to the S06tailscaled service for Kernel mode elif [ "$tsoperatingmode" == "Kernel" ]; then applykernelmode #make mods to the S06tailscaled service for Custom mode elif [ "$tsoperatingmode" == "Custom" ]; then applycustomchanges fi startts tsup echo "" printf "\33[2K\r" printf "${CGreen}\r[Tailscale Service/Connection Successfully Restarted]${CClear}" echo -e "\n" read -rsp $'Press any key to continue...\n' -n1 key } # ------------------------------------------------------------------------------------------------------------------------- # Tailscale reset connection routine tsreset() { printf "\33[2K\r" printf "${CGreen}\r[Initiating Forced Tailscale Connection Reset]" sleep 1 echo -e "\n" echo -e "${CRed}WARNING:${CClear} Executing this function will send a 'tailscale up --reset' command which " echo -e "will reset any default switches that are configured on your Tailscale connection. " echo -e "This action may be necessary at times when these switches are inadvertently set and " echo -e "registered with Tailscale, or due to switch functionality being altered or changed " echo -e "by the Tailscale developers themselves. Once the '--reset' switch has been sent, " echo -e "TAILMON will reinitialize the connection back to its regular defaults." echo "" echo -e "${CRed}PLEASE NOTE:${CClear} If you have configured any custom commandline switches that you want " echo -e "to reset, you would need to run your own custom Tailscale command in a separate " echo -e "prompt to disable the switch that is currently enabled. Please know that the switch " echo -e "itself is not removed, but basically disabled. Please consider finding more info at " echo -e "the https://tailscale.com/kb site for other references. Examples: " echo -e "${CGreen}tailscale up --accept-routes=false${CClear} -or-" echo -e "${CGreen}tailscale up --advertise-routes=${CClear}" echo "" echo -e "Reset Tailscale Connection?" if promptyn "[y/n]: " then echo -e "\n" tsdown echo "Executing: tailscale up --reset" echo "" tailscale up --reset echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Connection Reset using --reset switch." >> $logfile echo "" tsdown tsup echo "" printf "\33[2K\r" printf "${CGreen}\r[Tailscale Connection Successfully Reset]${CClear}" echo -e "\n" read -rsp $'Press any key to continue...\n' -n1 key fi } # ------------------------------------------------------------------------------------------------------------------------- # Tailscale connection reset tsresetc() { printf "\33[2K\r" printf "${CGreen}\r[Resetting Tailscale Connection]" sleep 1 printf "\33[2K\r" echo -e "${CGreen}Messages:${CClear}" echo "" echo "Executing: tailscale up --reset" echo "" tailscale up --reset echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Connection Reset using --reset switch." >> $logfile resettimer=1 echo -e "\n" read -rsp $'Press any key to continue...\n' -n1 key } # ------------------------------------------------------------------------------------------------------------------------- # Tailscale connection up tsup() { printf "\33[2K\r" printf "${CGreen}\r[Activating Tailscale Connection]" sleep 1 printf "\33[2K\r" if [ $exitnode -eq 1 ]; then exitnodecmd="--advertise-exit-node "; else exitnodecmd=""; fi if [ $advroutes -eq 1 ]; then advroutescmd="--advertise-routes=$routes "; else advroutescmd=""; fi if [ $accroutes -eq 1 ]; then accroutescmd="--accept-routes"; else accroutescmd=""; fi echo -e "${CGreen}Messages:${CClear}" echo "" if [ "$tsoperatingmode" == "Custom" ]; then echo "Executing: tailscale up $customcmdline" echo "" tailscale up $customcmdline tsstat=$? if [ "$tsstat" -ne 0 ]; then echo -e "${CRed}ERROR: Tailscale Connection did not start correctly${CClear}" echo "" #Display a standard timer# timer=0 while [ $timer -ne 5 ] do timer="$((timer+1))" preparebar 46 "|" progressbarpause $timer 5 "" "s" "Standard" done printf "\33[2K\r" fi else echo "Executing: tailscale up $exitnodecmd$advroutescmd$accroutescmd" echo "" tailscale up $exitnodecmd$advroutescmd$accroutescmd tsstat=$? if [ "$tsstat" -ne 0 ]; then echo -e "${CRed}ERROR: Tailscale Connection did not start correctly${CClear}" echo "" #Display a standard timer# timer=0 while [ $timer -ne 5 ] do timer="$((timer+1))" preparebar 46 "|" progressbarpause $timer 5 "" "s" "Standard" done printf "\33[2K\r" fi fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Connection started." >> $logfile resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # Tailscale connection down tsdown() { printf "\33[2K\r" printf "${CGreen}\r[Bringing Tailscale Connection Down]" sleep 1 printf "\33[2K\r" echo -e "${CGreen}Messages:${CClear}" echo "" echo "Executing: tailscale down" echo "" tailscale down echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale Connection stopped." >> $logfile resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # Force Tailscale Binary update tsupdate() { printf "\33[2K\r" printf "${CGreen}\r[Updating Tailscale Binary]" sleep 1 printf "\33[2K\r" echo -e "${CGreen}Messages:${CClear}" echo "" echo "Executing: tailscale update" echo "" tailscale update echo "" echo -e "Restart Tailscale?" if promptyn "[y/n]: " then echo ""; echo "" restarttsc fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale binary updated to latest available version." >> $logfile resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # Force Tailscale Binary update to latest BETA tsbeta() { printf "\33[2K\r" printf "${CGreen}\r[Updating Tailscale Binary to Latest BETA]" sleep 1 printf "\33[2K\r" echo -e "${CGreen}Messages:${CClear}" echo "" echo "Executing: tailscale update --track unstable" echo "" tailscale update --track unstable echo "" echo -e "Restart Tailscale?" if promptyn "[y/n]: " then echo ""; echo "" restarttsc fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale binary updated to latest BETA version." >> $logfile resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # autoupdate will automatically download and install new TAILMON scripts and Tailscale binaries - run via CRON job/switch autoupdate() { clear # Put TAILMON into maintenance mode echo > /jffs/addons/tailmon.d/updating.txt #Display tailmon client header echo -en "${InvGreen} ${InvDkGray} TAILMON - v" printf "%-8s" $version echo -e " ${CWhite}Run Auto Update${InvDkGray} $tzspaces$(date) ${CClear}" echo "" if [ "$updatetm" -eq 1 ] then printf "\33[2K\r" printf "${CGreen}\r[Checking Local TAILMON Version]" # Copy current version of script into a version file echo "$version" > "/jffs/addons/tailmon.d/localver.txt" sleep 1 printf "\33[2K\r" # Download the latest version file from the source repository if [ "$track" = "1" ] then printf "${CGreen}\r[Checking TAILMON BETA Version]" curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/develop/version.txt" -o "/jffs/addons/tailmon.d/beta.txt" else printf "${CGreen}\r[Checking Official TAILMON Version]" curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/main/version.txt" -o "/jffs/addons/tailmon.d/version.txt" fi sleep 1 officialverchk=$? if [ $officialverchk -ne 0 ] then printf "\33[2K\r" printf "${CGreen}\r[Unable to Determine TAILMON Version...Exiting]\n" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Unable to determine TAILMON version -- please check your internet connection. Autoupdate exiting." >> $logfile echo -e "${CClear}" sendmessage 1 "Unable to reach TAILMON repository" sleep 1 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 1 fi sleep 1 printf "\33[2K\r" printf "${CGreen}\r[Comparing TAILMON Versions]" sleep 1 # Check differences in version and download if newer official version is present if [ "$track" = "1" ]; then localver=$(cat "/jffs/addons/tailmon.d/localver.txt") serverver=$(cat "/jffs/addons/tailmon.d/beta.txt") else localver=$(cat "/jffs/addons/tailmon.d/localver.txt") serverver=$(cat "/jffs/addons/tailmon.d/version.txt") fi if [ "$localver" != "$serverver" ] then printf "\33[2K\r" if [ "$track" = "1" ] then printf "${CGreen}\r[Downloading New TAILMON BETA v$serverver]\n" curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/develop/tailmon.sh" -o "/jffs/scripts/tailmon.sh" && chmod 755 "/jffs/scripts/tailmon.sh" else printf "${CGreen}\r[Downloading New TAILMON STABLE v$serverver]\n" curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/main/tailmon.sh" -o "/jffs/scripts/tailmon.sh" && chmod 755 "/jffs/scripts/tailmon.sh" fi echo -e "${CClear}" sleep 1 officialver=$? if [ $officialver -ne 0 ] then printf "\33[2K\r" printf "${CGreen}\r[Unable to Download TAILMON...Exiting]\n" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Unable to download TAILMON -- please check your internet connection. Autoupdate exiting." >> $logfile echo -e "${CClear}" sendmessage 1 "Unable to reach TAILMON repository" sleep 1 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 1 fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Successfully autoupdated TAILMON from v$localver to v$serverver" >> $logfile sendmessage 0 "TAILMON Script Successfully Updated" $localver $serverver echo > /jffs/addons/tailmon.d/updated.txt else printf "\33[2K\r" printf "${CGreen}\r[Local TAILMON Version is the Latest Available]\n" echo -e "${CClear}" sleep 1 fi sleep 1 fi if [ "$updatets" -eq 1 ] then printf "\33[2K\r" printf "${CGreen}\r[Checking Local Tailscale Version]" sleep 1 # Checking for local Tailscale version echo $(tailscale version | awk 'NR==1 {print $1}') > /jffs/addons/tailmon.d/localtsver.txt localtsverchk=$? if [ $localtsverchk -ne 0 ] then printf "\33[2K\r" printf "${CGreen}\r[Unable to Determine Local Tailscale Version...Exiting]\n" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Unable to determine local Tailscale version -- please check your installation. Autoupdate exiting." >> $logfile echo -e "${CClear}" sleep 2 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 1 fi sleep 1 printf "\33[2K\r" printf "${CGreen}\r[Checking Official Tailscale Version]" sleep 1 # Checking for upstream Tailscale version echo $(tailscale version --upstream | grep "upstream" | cut -d ':' -f 2) > /jffs/addons/tailmon.d/tsversion.txt upstreamtsverchk=$? if [ $upstreamtsverchk -ne 0 ] then printf "\33[2K\r" printf "${CGreen}\r[Unable to Determine Official Tailscale Version...Exiting]\n" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Unable to determine Official Tailscale version -- please check your installation/internet connection. Autoupdate exiting." >> $logfile echo -e "${CClear}" sendmessage 1 "Unable to reach Tailscale repository" sleep 1 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 1 fi sleep 1 printf "\33[2K\r" printf "${CGreen}\r[Comparing Tailscale Versions]" sleep 1 # Check differences in version and download if newer official version is present localtsver=$(cat "/jffs/addons/tailmon.d/localtsver.txt") servertsver=$(cat "/jffs/addons/tailmon.d/tsversion.txt") if [ "$localtsver" != "$servertsver" ] then printf "\33[2K\r" printf "${CGreen}\r[Downloading New Tailscale Binary v$servertsver]\n" echo -e "${CClear}" sleep 1 tailscale update -yes officialtsver=$? if [ $officialtsver -ne 0 ] then printf "\33[2K\r" printf "${CGreen}\r[Unable to Download Tailscale Binary...Exiting]\n" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Unable to download Tailscale Binary - please check your installation/internet connection." >> $logfile echo -e "${CClear}" sendmessage 1 "Unable to reach Tailscale repository" sleep 1 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 1 fi echo "" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Successfully autoupdated Tailscale Binary from v$localtsver to v$servertsver" >> $logfile sendmessage 0 "Tailscale Successfully Updated" $localtsver $servertsver # Upon a successful update, restart Tailscale services echo ""; echo "" printf "\33[2K\r" printf "${CGreen}\r[Restarting Tailscale Service/Connection]\n" echo -e "${CClear}" sleep 1 tsdown stopts #make mods to the S06tailscaled service for Userspace mode if [ "$tsoperatingmode" == "Userspace" ]; then applyuserspacemode #make mods to the S06tailscaled service for Kernel mode elif [ "$tsoperatingmode" == "Kernel" ]; then applykernelmode #make mods to the S06tailscaled service for Custom mode elif [ "$tsoperatingmode" == "Custom" ]; then applycustomchanges fi startts tsup echo "" printf "\33[2K\r" printf "${CGreen}\r[Tailscale Service/Connection Successfully Restarted]\n" echo -e "${CClear}" sleep 1 printf "\33[2K\r" printf "${CGreen}\r[Autoupdate Completed Successfully]\n" echo -e "${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Autoupdate completed successfully." >> $logfile sleep 1 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 0 else printf "\33[2K\r" printf "${CGreen}\r[Local Tailscale Version is the Latest Available...Exiting]\n" echo -e "${CClear}" sleep 1 fi fi printf "\33[2K\r" printf "${CGreen}\r[Autoupdate Completed Successfully]\n" echo -e "${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Autoupdate completed successfully." >> $logfile sleep 1 rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 exit 0 } # ------------------------------------------------------------------------------------------------------------------------- # Force Downgrade Tailscale Binary update check_url() { # Using curl to check for a valid version archive curl -s --head --fail "$1" >/dev/null 2>&1 } tsdowngrade() { printf "\33[2K\r" printf "${CGreen}\r[Downgrading Tailscale Binary]" sleep 1 echo ""; echo "" echo -e "${CGreen}Messages:${CClear}" while true; do # Prompt the user for the Tailscale version. echo "" printf "Please enter the Tailscale version to downgrade to (ex: 1.84.0, e=Exit): " read -r TS_VERSION if [ -z "$TS_VERSION" ]; then echo "" echo -e "${CRed}No version entered. Please try again.${CClear}" echo "" continue elif [ "$TS_VERSION" = "e" ]; then echo "" echo -e "${CClear}[Exiting]" sleep 1 return fi echo "" echo -e "${CClear}Are you downgrading to a Tailscale Beta Version? (y=Beta, n=Stable)?" TS_BETA=0 if promptyn "[y/n]: " then TS_BETA=1 fi # Determine system architecture to build the correct download URL. ARCH=$(uname -m) case $ARCH in "aarch64" | "arm64") TS_ARCH="arm64" ;; "armv7l") TS_ARCH="arm" ;; *) echo ""; echo "" echo -e "${CRed}Not sure how you did it, but you're running an unsupported architecture: $ARCH ${CClear}" sleep 2 exit 1 ;; esac # Construct the download URL. if [ "$TS_BETA" -eq 1 ]; then DOWNLOAD_URL="https://pkgs.tailscale.com/unstable/tailscale_${TS_VERSION}_${TS_ARCH}.tgz" else DOWNLOAD_URL="https://pkgs.tailscale.com/stable/tailscale_${TS_VERSION}_${TS_ARCH}.tgz" fi # Validate the version by checking if the URL is reachable. echo ""; echo "" echo -e "${CGreen}Verifying version:${CClear} $TS_VERSION ${CGreen}for architecture:${CClear} $TS_ARCH" echo "" if ! check_url "$DOWNLOAD_URL"; then echo -e "------------------------------------------------------------------" echo -e "${CRed}Error: Invalid version or version not found for your architecture.${CClear}" echo -e "URL checked: $DOWNLOAD_URL" echo -e "Please check the version number and try again." echo -e "------------------------------------------------------------------" continue # Go back to the start of the loop fi echo -e "${CGreen}Version is valid. Proceeding with download...${CClear}" echo "" # Define file paths TMP_DIR="/tmp" DOWNLOAD_PATH="$TMP_DIR/tailscale_${TS_VERSION}_${TS_ARCH}.tgz" EXTRACT_DIR="$TMP_DIR/tailscale_${TS_VERSION}_${TS_ARCH}" DEST_DIR="/opt/bin" # Download the package to the /tmp folder. echo -e "${CGreen}Downloading from:${CClear} $DOWNLOAD_URL" echo "" if ! curl -L -o "$DOWNLOAD_PATH" "$DOWNLOAD_URL"; then echo "" echo -e "${CRed}Error: Download failed. Please check your internet connection.${CClear}" continue fi echo "" echo -e "${CGreen}Download complete.${CClear}" echo "" # Extract the 'tailscale' and 'tailscaled' binaries. echo -e "${CGreen}Extracting Tailscale binaries to:${CClear} $TMP_DIR" echo "" # Extract the whole archive and then find our files. if ! tar -xzf "$DOWNLOAD_PATH" -C "$TMP_DIR"; then echo -e "${CRed}Error: Extraction failed.${CClear}" rm -f "$DOWNLOAD_PATH" # Clean up failed download continue fi # The extracted files should be inside a directory like /tmp/tailscale_1.84.0 SOURCE_TAILSCALE="$EXTRACT_DIR/tailscale" SOURCE_TAILSCALED="$EXTRACT_DIR/tailscaled" # Verify that the binaries were extracted if [ ! -f "$SOURCE_TAILSCALE" ] || [ ! -f "$SOURCE_TAILSCALED" ]; then echo -e "${CRed}Error: The required binaries 'tailscale' or 'tailscaled' were not found in the archive.${CClear}" # Clean up rm -f "$DOWNLOAD_PATH" rm -rf "$EXTRACT_DIR" continue fi echo -e "${CGreen}Extraction successful.${CClear}" echo "" # Check if the destination directory exists. if [ ! -d "$DEST_DIR" ]; then echo -e "{$CRed}Destination directory${CClear} $DEST_DIR {$CRed}does not exist. Please install Entware...${CClear}" exit 1 fi # Stop any running Tailscale serices echo -e "${CGreen}Stopping current Tailscale Service and Connection...${CClear}" echo "" tsdown stopts # Delete the existing files if they exist. echo -e "${CGreen}Removing current Tailscale versions from${CClear} $DEST_DIR..." echo "" rm -f "$DEST_DIR/tailscale" rm -f "$DEST_DIR/tailscaled" # Move the two extracted files to /opt/bin. echo -e "${CGreen}Moving downloaded Tailscale binaries to${CClear} $DEST_DIR..." echo "" if ! mv "$SOURCE_TAILSCALE" "$DEST_DIR/"; then echo -e "${CRed}Error moving tailscale binary.${CClear}" # Clean up rm -f "$DOWNLOAD_PATH" rm -rf "$EXTRACT_DIR" continue fi if ! mv "$SOURCE_TAILSCALED" "$DEST_DIR/"; then echo -e "${CRed}Error moving tailscaled binary.${CClear}" # Clean up rm -f "$DOWNLOAD_PATH" rm -rf "$EXTRACT_DIR" continue fi # Make them both executable. echo -e "${CGreen}Setting Tailscale permissions...${CClear}" echo "" chmod 755 "$DEST_DIR/tailscale" chmod 755 "$DEST_DIR/tailscaled" echo -e "${CGreen}Tailscale has been successfully updated to${CClear} $TS_VERSION ${CGreen}and installed to${CClear} $DEST_DIR." echo "" # Clean up the downloaded archive and extracted folder echo -e "${CGreen}Cleaning up temporary files...${CClear}" echo "" rm -f "$DOWNLOAD_PATH" rm -rf "$EXTRACT_DIR" # Exit the loop on success break done echo -e "${CClear}Restart Tailscale using downgraded version $TS_VERSION?" if promptyn "[y/n]: " then echo ""; echo "" restarttsc fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Tailscale binaries successfully updated to $TS_VERSION" >> $logfile resettimer=1 } # ------------------------------------------------------------------------------------------------------------------------- # schedulevpnreset lets you enable and set a time for a scheduled daily vpn reset ##----------------------------------------## ## Modified by Martinski W. [2024-Oct-06] ## ##----------------------------------------## scheduleautoupdates() { while true do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} TAILMON Autoupdate Scheduler ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate below if you would like to enable and schedule a daily autoupdate CRON" echo -e "${InvGreen} ${CClear} job. This can check for both TAILMON and Tailscale updates. Please NOTE: Autoupdate" echo -e "${InvGreen} ${CClear} will only update to the latest stable release. Beta updates need to handled manually" echo -e "${InvGreen} ${CClear} using the option in the Main Setup & Configuration menu." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Autoupdate Default = Disabled)" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to enable/disable autoupdates:${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" if [ "$updatetm" == "1" ]; then updatetmdisp="${CGreen}Enabled${CCyan}"; else updatetm=0; updatetmdisp="${CRed}Disabled${CCyan}"; fi if [ "$updatets" == "1" ]; then updatetsdisp="${CGreen}Enabled${CCyan}"; else updatets=0; updatetsdisp="${CRed}Disabled${CCyan}"; fi echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Autoupdate TAILMON Script ${CClear} ${CGreen}(1) -${CClear} $updatetmdisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Autoupdate Tailscale Binary${CClear} ${CGreen}(2) -${CClear} $updatetsdisp${CClear}" echo -e "${InvGreen} ${CClear}" if [ "$schedule" = "0" ] then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Enable Autoupdate Schedule ${CClear} ${CGreen}(Y/N) - ${CRed}Disabled${CClear}" elif [ "$schedule" = "1" ] then schedhrs="$(awk "BEGIN {printf \"%02.f\",${schedulehrs}}")" schedmin="$(awk "BEGIN {printf \"%02.f\",${schedulemin}}")" schedtime="${CGreen}$schedhrs:$schedmin${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Enable Autoupdate Schedule ${CClear} ${CGreen}(Y/N) - Enabled, Daily @ $schedtime${CClear}" fi echo "" read -p "Please select? (1-2, n=Disable Schedule, y=Enable Schedule, e=Exit): " newSchedule case $newSchedule in 1) if [ "$updatetm" == "0" ]; then updatetm=1; updatetmdisp="${CGreen}Enabled${CCyan}"; elif [ "$updatetm" == "1" ]; then updatetm=0; updatetmdisp="${CRed}Disabled${CCyan}"; fi; saveconfig;; 2) if [ "$updatets" == "0" ]; then updatets=1; updatetsdisp="${CGreen}Enabled${CCyan}"; elif [ "$updatets" == "1" ]; then updatets=0; updatetsdisp="${CRed}Disabled${CCyan}"; fi; saveconfig;; [Nn]) schedule=0 if [ -f /jffs/scripts/services-start ] then sed -i -e '/tailmon.sh/d' /jffs/scripts/services-start cru d RunTAILMONcheck schedulehrs=1 schedulemin=0 echo "" echo -e "${CGreen}[Modifiying SERVICES-START file]..." sleep 2 echo "" echo -e "${CGreen}[Modifying CRON jobs]..." sleep 2 echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) TAILMON[$$] - INFO: Autoupdate Scheduled Check Disabled" >> $logfile saveconfig fi ;; [Yy]) schedule=1 echo "" echo -e "${InvGreen} ${InvDkGray}${CWhite} Select CRON Job Time ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate below what time you would like to schedule a daily Autoupdate CRON" echo -e "${InvGreen} ${CClear} job. (Default = 1 hr, 0 min = 01:00 = 1:00am)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo read -p 'Schedule HOURS [0-23]?: ' newScheduleHrs if [ -z "$newScheduleHrs" ] then if _ValidateCronJobHour_ "$schedulehrs" then scheduleHrsOK=true else scheduleHrsOK=false fi elif _ValidateCronJobHour_ "$newScheduleHrs" then scheduleHrsOK=true schedulehrs="$newScheduleHrs" else scheduleHrsOK=false schedulehrs="${schedulehrs:=1}" printf "${CRed}*ERROR*: INVALID Entry.${CClear}\n\n" fi read -p 'Schedule MINUTES [0-59]?: ' newScheduleMins if [ -z "$newScheduleMins" ] then if _ValidateCronJobMinute_ "$schedulemin" then scheduleMinsOK=true else scheduleMinsOK=false fi elif _ValidateCronJobMinute_ "$newScheduleMins" then scheduleMinsOK=true schedulemin="$newScheduleMins" else scheduleMinsOK=false schedulemin="${schedulemin:=0}" printf "${CRed}*ERROR*: INVALID Entry.${CClear}\n" fi if ! "$scheduleHrsOK" || ! "$scheduleMinsOK" then doResetSave=false if ! "$scheduleHrsOK" && ! _ValidateCronJobHour_ "$schedulehrs" then schedulehrs=1 ; doResetSave=true fi if ! "$scheduleMinsOK" && ! _ValidateCronJobMinute_ "$schedulemin" then schedulemin=0 ; doResetSave=true fi if "$doResetSave" then schedule=0 saveconfig printf "\n${CRed}INVALID input found. Resetting values.${CClear}\n\n" else printf "\n${CRed}INVALID input found. No changes made.${CClear}\n\n" fi echo -e "${CClear}[Exiting]" timer="$timerloop" sleep 3 break fi echo echo -e "${CGreen}[Modifying SERVICES-START file]..." sleep 2 if [ -f /jffs/scripts/services-start ] then if ! grep -q -F "sh /jffs/scripts/tailmon.sh -autoupdate" /jffs/scripts/services-start then echo 'cru a RunTAILMONcheck "'"$schedulemin $schedulehrs * * * sh /jffs/scripts/tailmon.sh -autoupdate"'"' >> /jffs/scripts/services-start cru a RunTAILMONcheck "$schedulemin $schedulehrs * * * sh /jffs/scripts/tailmon.sh -autoupdate" else #delete and re-add if it already exists in case there's a time change sed -i -e '/tailmon.sh/d' /jffs/scripts/services-start cru d RunTAILMONcheck echo 'cru a RunTAILMONcheck "'"$schedulemin $schedulehrs * * * sh /jffs/scripts/tailmon.sh -autoupdate"'"' >> /jffs/scripts/services-start cru a RunTAILMONcheck "$schedulemin $schedulehrs * * * sh /jffs/scripts/tailmon.sh -autoupdate" fi else echo 'cru a RunTAILMONcheck "'"$schedulemin $schedulehrs * * * sh /jffs/scripts/tailmon.sh -autoupdate"'"' >> /jffs/scripts/services-start chmod 755 /jffs/scripts/services-start cru a RunTAILMONcheck "$schedulemin $schedulehrs * * * sh /jffs/scripts/tailmon.sh -autoupdate" fi echo echo -e "${CGreen}[Modifying CRON jobs]..." sleep 2 echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) TAILMON[$$] - INFO: Autoupdate Scheduled Check Enabled" >> $logfile saveconfig ;; [Ee]) echo ; echo -e "${CClear}[Exiting]" sleep 2 saveconfig return ;; esac done } ##-------------------------------------## ## Added by Martinski W. [2024-Oct-06] ## ##-------------------------------------## _ValidateCronJobHour_() { if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi if echo "$1" | grep -qE "^(0|[1-9][0-9]?)$" && \ [ "$1" -ge 0 ] && [ "$1" -lt 24 ] then return 0 ; else return 1 ; fi } _ValidateCronJobMinute_() { if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi if echo "$1" | grep -qE "^(0|[1-9][0-9]?)$" && \ [ "$1" -ge 0 ] && [ "$1" -lt 60 ] then return 0 ; else return 1 ; fi } ##-------------------------------------## ## Added by Martinski W. [2024-Oct-05] ## ##-------------------------------------## _SetLAN_HostName_() { [ -z "${LAN_HostName:+xSETx}" ] && \ LAN_HostName="$($timeoutcmd$timeoutsec nvram get lan_hostname)" } _GetLAN_HostName_() { _SetLAN_HostName_ ; echo "$LAN_HostName" ; } # ------------------------------------------------------------------------------------------------------------------------- # autostart lets you enable the ability for tailmon to autostart after a router reboot autostart() { while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Reboot Protection ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate below if you would like to enable TAILMON to autostart after a" echo -e "${InvGreen} ${CClear} router reboot. This will ensure continued, uninterrupted Tailscale monitoring." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = Disabled)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" if [ "$autostart" == "0" ]; then echo -e "${InvGreen} ${CClear} Current: ${CRed}Disabled${CClear}" elif [ "$autostart" == "1" ]; then echo -e "${InvGreen} ${CClear} Current: ${CGreen}Enabled${CClear}" fi echo "" read -p 'Enable Reboot Protection? (0=No, 1=Yes, e=Exit): ' autostart1 if [ "$autostart1" == "" ] || [ -z "$autostart1" ]; then autostart=0; else autostart="$autostart1"; fi # Using default value on enter keypress if [ "$autostart" == "0" ]; then if [ -f /jffs/scripts/post-mount ]; then sed -i -e '/tailmon.sh/d' /jffs/scripts/post-mount autostart=0 echo "" echo -e "${CGreen}[Modifying POST-MOUNT file]..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Reboot Protection Disabled" >> $logfile saveconfig sleep 1 timer=$timerloop break fi elif [ "$autostart" == "1" ]; then if [ -f /jffs/scripts/post-mount ]; then if ! grep -q -F "(sleep 30 && /jffs/scripts/tailmon.sh -screen) & # Added by tailmon" /jffs/scripts/post-mount; then echo "(sleep 30 && /jffs/scripts/tailmon.sh -screen) & # Added by tailmon" >> /jffs/scripts/post-mount autostart=1 echo "" echo -e "${CGreen}[Modifying POST-MOUNT file]..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Reboot Protection Enabled" >> $logfile saveconfig sleep 1 timer=$timerloop break else autostart=1 saveconfig sleep 1 fi else echo "#!/bin/sh" > /jffs/scripts/post-mount echo "" >> /jffs/scripts/post-mount echo "(sleep 30 && /jffs/scripts/tailmon.sh -screen) & # Added by tailmon" >> /jffs/scripts/post-mount chmod 755 /jffs/scripts/post-mount autostart=1 echo "" echo -e "${CGreen}[Modifying POST-MOUNT file]..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Reboot Protection Enabled" >> $logfile saveconfig sleep 1 timer=$timerloop break fi elif [ "$autostart" == "e" ]; then timer=$timerloop break else autostart=0 saveconfig fi done } # ------------------------------------------------------------------------------------------------------------------------- # timerloopconfig lets you configure how long you want the timer cycle to last between tailscale checks timerloopconfig() { while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Timer Loop Configuration ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate how long the timer cycle should take between Tailscale Service and.${CClear}" echo -e "${InvGreen} ${CClear} Connection checks." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = 60 seconds)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current: ${CGreen}$timerloop sec${CClear}" echo "" read -p "Please enter value (1-999)? (e=Exit): " EnterTimerLoop case $EnterTimerLoop in [1-9]) timerloop=$EnterTimerLoop saveconfig timer=$timerloop ;; [1-9][0-9]) timerloop=$EnterTimerLoop saveconfig timer=$timerloop ;; [1-9][0-9][0-9]) timerloop=$EnterTimerLoop saveconfig timer=$timerloop ;; *) echo "" echo -e "${CClear}[Exiting]" timer=$timerloop break ;; esac done } # ------------------------------------------------------------------------------------------------------------------------- # customconfig lets you edit the args and settings for tailscale customconfig() { restartts=0 while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Custom Tailscale Configuration ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} This functionality allows you to choose your own Tailscale ARGS, PREARGS and PRECMD${CClear}" echo -e "${InvGreen} ${CClear} entries, and allows you to modify the Tailscale connection commandline options.${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${CYellow}Proceed at your own risk!${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current Operating Mode: ${CGreen}$tsoperatingmode${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current values in Tailscale Service (/opt/etc/init.d/S06tailscaled):${CClear}" s06args=$(cat /opt/etc/init.d/S06tailscaled | grep ^ARGS= | cut -d '=' -f 2-) 2>/dev/null if [ -z "$s06args" ]; then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(1)${CClear} ${CGreen}ARGS=\"\"${CClear}" else echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(1)${CClear} ${CGreen}ARGS=$s06args${CClear}" fi s06preargs=$(cat /opt/etc/init.d/S06tailscaled | grep ^PREARGS= | cut -d '=' -f 2-) 2>/dev/null if [ -z "$s06preargs" ]; then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(2)${CClear} ${CGreen}PREARGS=\"\"${CClear}" else echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(2)${CClear} ${CGreen}PREARGS=$s06preargs${CClear}" fi s06precmd=$(cat /opt/etc/init.d/S06tailscaled | grep ^PRECMD= | cut -d '=' -f 2-) 2>/dev/null if [ -z "$s06precmd" ]; then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(3)${CClear} ${CGreen}PRECMD=\"\"${CClear}" else echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(3)${CClear} ${CGreen}PRECMD=$s06precmd${CClear}" fi echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current custom values being used for Tailscale Connection commandline:${CClear}" if [ -z "$customcmdline" ]; then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(4)${CClear} ${CGreen}CMD=\"\"${CClear}" else echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(4)${CClear} ${CGreen}CMD=\"$customcmdline\"${CClear}" fi echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" read -p "Please enter item to modify (1-4)? (e=Exit): " EnterTimerLoop case $EnterTimerLoop in 1) echo "" echo -e "${CClear}When entering a custom statement, please do not use quotes or other abnormal characters." echo -e "${CClear}Example: --tun=userspace-networking --state=/opt/var/tailscaled.state --statedir=/opt/var/lib/tailscale" echo "" read -p "Enter new ARGS= " EnterNewArgs tsoperatingmode="Custom" args=$EnterNewArgs args_regexp="$(printf '%s' "$args" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')" sed -i "s/^ARGS=.*/ARGS=\"$args_regexp\"/" "/opt/etc/init.d/S06tailscaled" saveconfig timer=$timerloop restartts=1 ;; 2) echo "" echo -e "${CClear}When entering a custom statement, please do not use quotes or other abnormal characters." echo -e "${CClear}Example: nohup" echo "" read -p "Enter new PREARGS= " EnterNewPreArgs tsoperatingmode="Custom" preargs=$EnterNewPreArgs preargs_regexp="$(printf '%s' "$preargs" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')" sed -i "s/^PREARGS=.*/PREARGS=\"$preargs_regexp\"/" "/opt/etc/init.d/S06tailscaled" saveconfig timer=$timerloop restartts=1 ;; 3) echo "" echo -e "${CClear}When entering a custom statement, please do not use quotes or other abnormal characters." echo -e "${CClear}Example: modprobe tun" echo "" read -p "Enter new PRECMD= " EnterNewPreCmd tsoperatingmode="Custom" precmd=$EnterNewPreCmd precmd_regexp="$(printf '%s' "$precmd" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')" if ! grep -q -F "PRECMD=" /opt/etc/init.d/S06tailscaled; then sed '5 i PRECMD=\"'"$precmd_regexp"'\"' /opt/etc/init.d/S06tailscaled > /opt/etc/init.d/S06tailscaled2 rm -f /opt/etc/init.d/S06tailscaled mv /opt/etc/init.d/S06tailscaled2 /opt/etc/init.d/S06tailscaled chmod 755 /opt/etc/init.d/S06tailscaled else sed -i "s/^PRECMD=.*/PRECMD=\"$precmd_regexp\"/" "/opt/etc/init.d/S06tailscaled" fi saveconfig timer=$timerloop restartts=1 ;; 4) echo "" echo -e "${CClear}When entering a custom statement, please do not use quotes or other abnormal characters." echo -e "${CClear}Example: --advertise-exit-node --advertise-routes=192.168.50.0/24,192.168.87.0/24" echo "" read -p "Enter new Commandline Options: " EnterNewCmdOptions tsoperatingmode="Custom" customcmdline=$EnterNewCmdOptions saveconfig timer=$timerloop restartts=1 ;; *) if [ -f "/opt/bin/tailscale" ]; then if [ $restartts -eq 1 ]; then echo "" echo -e "Changing custom configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsup fi fi fi echo "" echo -e "${CClear}[Exiting]" timer=$timerloop break ;; esac done } # ------------------------------------------------------------------------------------------------------------------------- # operating mode lets the user choose between userspace and kernel modes of operation operatingmode() { restartts=0 while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Operating Mode Configuration ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Tailscale has 2 main modes of operation: 'Userspace' and 'Kernel' mode. By default,${CClear}" echo -e "${InvGreen} ${CClear} the installer will configure Tailscale to operate in 'Userspace' mode, but in the${CClear}" echo -e "${InvGreen} ${CClear} end, should not make much difference performance-wise based on the hardware available${CClear}" echo -e "${InvGreen} ${CClear} in our routers. More info below:${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} In general, kernel mode (and thus only Linux, for now) should be used for heavily${CClear}" echo -e "${InvGreen} ${CClear} used subnet routers, where 'heavy' is some combination of number of users, number${CClear}" echo -e "${InvGreen} ${CClear} of flows, bandwidth. The userspace mode should be more than sufficient for smaller${CClear}" echo -e "${InvGreen} ${CClear} numbers of users or low bandwidth. Even though Tailscale's userspace subnet routing${CClear}" echo -e "${InvGreen} ${CClear} is not as optimized as the Linux kernel, it makes up for it slightly in being able${CClear}" echo -e "${InvGreen} ${CClear} to avoid some context switches to the kernel.${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} A 3rd option (Custom) is also available, that allows you to enter your own custom${CClear}" echo -e "${InvGreen} ${CClear} settings for the ARGS, PREARGS, PRECMD and Tailscale Commandline. ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CYellow} NOTE: TAILMON will apply changes to modes after hitting the (e)xit key. If 'Custom'${CClear}" echo -e "${InvGreen} ${CClear}${CYellow} operating mode is chosen, you will be presented with the option to edit custom${CClear}" echo -e "${InvGreen} ${CClear}${CYellow} Tailscale settings after changes have been applied.${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = Userspace Mode)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current: ${CGreen}$tsoperatingmode${CClear}" echo "" read -p "Please enter value (1=Userspace, 2=Kernel, 3=Custom)? (e=Exit/Apply Changes): " EnterOperatingMode case $EnterOperatingMode in 1) if [ "$tsoperatingmode" != "Userspace" ]; then restartts=1; fi echo -e "\n${CGreen}[Userspace Operating Mode Selected]" sleep 1 tsoperatingmode="Userspace" precmd="" args="--tun=userspace-networking --state=/opt/var/tailscaled.state --statedir=/opt/var/lib/tailscale" preargs="nohup" customcmdline="" saveconfig timer=$timerloop ;; 2) if [ "$tsoperatingmode" != "Kernel" ]; then restartts=1; fi echo -e "\n${CGreen}[Kernel Operating Mode Selected]" sleep 1 tsoperatingmode="Kernel" precmd="modprobe tun" args="--state=/opt/var/tailscaled.state --statedir=/opt/var/lib/tailscale" preargs="nohup" customcmdline="" saveconfig timer=$timerloop ;; 3) if [ "$tsoperatingmode" != "Custom" ]; then restartts=1; fi echo -e "\n${CGreen}[Custom Operating Mode Selected]" sleep 1 tsoperatingmode="Custom" precmd="modprobe tun" args="--state=/opt/var/tailscaled.state --statedir=/opt/var/lib/tailscale" preargs="nohup" if [ $exitnode -eq 1 ]; then exitnodecmd="--advertise-exit-node "; else exitnodecmd=""; fi if [ $advroutes -eq 1 ]; then advroutescmd="--advertise-routes=$routes"; else advroutescmd=""; fi customcmdline="$exitnodecmd$advroutescmd" saveconfig timer=$timerloop ;; *) if [ -f "/opt/bin/tailscale" ]; then if [ $restartts -eq 1 ]; then echo "" echo -e "Changing operating modes will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts #make mods to the S06tailscaled service for Userspace mode if [ "$tsoperatingmode" == "Userspace" ]; then applyuserspacemode #make mods to the S06tailscaled service for Kernel mode elif [ "$tsoperatingmode" == "Kernel" ]; then applykernelmode #make mods to the S06tailscaled service for Custom mode elif [ "$tsoperatingmode" == "Custom" ]; then applycustommode fi startts tsup if [ "$tsoperatingmode" == "Custom" ]; then echo "" echo -e "Would you like to customize your Tailscale settings now?" if promptyn "[y/n]: " then customconfig fi fi fi echo "" echo -e "${CClear}[Exiting]" timer=$timerloop break else echo "" echo -e "${CClear}[Exiting]" timer=$timerloop break fi fi ;; esac done } # ------------------------------------------------------------------------------------------------------------------------- # applyuserspacemode applies the standard settings for the Userspace operating mode applyuserspacemode() { sed -i "s/^ARGS=.*/ARGS=\"--tun=userspace-networking\ --state=\/opt\/var\/tailscaled.state\ --statedir=\/opt\/var\/lib\/tailscale\"/" "/opt/etc/init.d/S06tailscaled" sed -i "s/^PREARGS=.*/PREARGS=\"nohup\"/" "/opt/etc/init.d/S06tailscaled" sed -i -e '/^PRECMD=/d' "/opt/etc/init.d/S06tailscaled" #remove firewall-start entry if found if [ -f /jffs/scripts/firewall-start ]; then if grep -q -F "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi" /jffs/scripts/firewall-start; then sed -i -e '/tailscale down/d' /jffs/scripts/firewall-start echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: firewall-start entries removed." >> $logfile fi fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Userspace Mode settings have been applied." >> $logfile } # ------------------------------------------------------------------------------------------------------------------------- # applykernelmode applies the standard settings for the Kernel operating mode applykernelmode() { if ! grep -q -F "PRECMD=" /opt/etc/init.d/S06tailscaled; then sed '5 i PRECMD=\"modprobe tun\"' /opt/etc/init.d/S06tailscaled > /opt/etc/init.d/S06tailscaled2 rm -f /opt/etc/init.d/S06tailscaled mv /opt/etc/init.d/S06tailscaled2 /opt/etc/init.d/S06tailscaled chmod 755 /opt/etc/init.d/S06tailscaled else sed -i "s/^PRECMD=.*/PRECMD=\"modprobe tun\"/" "/opt/etc/init.d/S06tailscaled" fi sed -i "s/^ARGS=.*/ARGS=\"--state=\/opt\/var\/tailscaled.state\ --statedir=\/opt\/var\/lib\/tailscale\"/" "/opt/etc/init.d/S06tailscaled" sed -i "s/^PREARGS=.*/PREARGS=\"nohup\"/" "/opt/etc/init.d/S06tailscaled" #modify/create firewall-start if [ -f /jffs/scripts/firewall-start ]; then if ! grep -q -F "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi" /jffs/scripts/firewall-start; then echo "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi # Added by TAILMON" >> /jffs/scripts/firewall-start echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: firewall-start entries created." >> $logfile fi else echo "#!/bin/sh" > /jffs/scripts/firewall-start echo "" >> /jffs/scripts/firewall-start echo "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi # Added by TAILMON" >> /jffs/scripts/firewall-start chmod 0755 /jffs/scripts/firewall-start fi echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Kernel Mode settings have been applied." >> $logfile } # ------------------------------------------------------------------------------------------------------------------------- # applycustommode applies the standard settings for the Custom operating mode which initially mimics Kernel mode applycustommode() { if ! grep -q -F "PRECMD=" /opt/etc/init.d/S06tailscaled; then sed '5 i PRECMD=\"modprobe tun\"' /opt/etc/init.d/S06tailscaled > /opt/etc/init.d/S06tailscaled2 rm -f /opt/etc/init.d/S06tailscaled mv /opt/etc/init.d/S06tailscaled2 /opt/etc/init.d/S06tailscaled chmod 755 /opt/etc/init.d/S06tailscaled else sed -i "s/^PRECMD=.*/PRECMD=\"modprobe tun\"/" "/opt/etc/init.d/S06tailscaled" fi sed -i "s/^ARGS=.*/ARGS=\"--state=\/opt\/var\/tailscaled.state\ --statedir=\/opt\/var\/lib\/tailscale\"/" "/opt/etc/init.d/S06tailscaled" sed -i "s/^PREARGS=.*/PREARGS=\"nohup\"/" "/opt/etc/init.d/S06tailscaled" #modify/create firewall-start if [ -f /jffs/scripts/firewall-start ]; then if ! grep -q -F "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi" /jffs/scripts/firewall-start; then echo "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi # Added by TAILMON" >> /jffs/scripts/firewall-start echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: firewall-start entries created." >> $logfile fi else echo "#!/bin/sh" > /jffs/scripts/firewall-start echo "" >> /jffs/scripts/firewall-start echo "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi # Added by TAILMON" >> /jffs/scripts/firewall-start chmod 0755 /jffs/scripts/firewall-start fi if [ $exitnode -eq 1 ]; then exitnodecmd="--advertise-exit-node "; else exitnodecmd=""; fi if [ $advroutes -eq 1 ]; then advroutescmd="--advertise-routes=$routes"; else advroutescmd=""; fi customcmdline="$exitnodecmd$advroutescmd" saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Custom Mode settings have been applied." >> $logfile } # ------------------------------------------------------------------------------------------------------------------------- # applycustomchanges applies the custom settings for the Custom operating mode that may have been changed by the user applycustomchanges() { precmd_regexp="$(printf '%s' "$precmd" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')" if ! grep -q -F "PRECMD=" /opt/etc/init.d/S06tailscaled; then sed '5 i PRECMD=\"'"$precmd_regexp"'\"' /opt/etc/init.d/S06tailscaled > /opt/etc/init.d/S06tailscaled2 rm -f /opt/etc/init.d/S06tailscaled mv /opt/etc/init.d/S06tailscaled2 /opt/etc/init.d/S06tailscaled chmod 755 /opt/etc/init.d/S06tailscaled else sed -i "s/^PRECMD=.*/PRECMD=\"$precmd_regexp\"/" "/opt/etc/init.d/S06tailscaled" fi args_regexp="$(printf '%s' "$args" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')" sed -i "s/^ARGS=.*/ARGS=\"$args_regexp\"/" "/opt/etc/init.d/S06tailscaled" preargs_regexp="$(printf '%s' "$preargs" | sed -e 's/[]\/$*.^|[]/\\&/g' | sed ':a;N;$!ba;s,\n,\\n,g')" sed -i "s/^PREARGS=.*/PREARGS=\"$preargs_regexp\"/" "/opt/etc/init.d/S06tailscaled" saveconfig timer=$timerloop restartts=1 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Custom Mode changes have been applied." >> $logfile } # ------------------------------------------------------------------------------------------------------------------------- # exitnodets provide a menu interface to allow for selection of router becoming an exitnode exitnodets() { clear if [ $exitnode -eq 0 ]; then exitnodedisp="No"; elif [ $exitnode -eq 1 ]; then exitnodedisp="Yes"; fi echo -e "${InvGreen} ${InvDkGray}${CWhite} Configure Router as Exit Node ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} A Tailscale Exit Node is a feature that lets you route all internet traffic," echo -e "${InvGreen} ${CClear} including internet traffic from non-Tailscale devices, through a specific device" echo -e "${InvGreen} ${CClear} on your Tailscale network (known as a tailnet). The device routing your traffic" echo -e "${InvGreen} ${CClear} (this router) is called an 'exit node'. Please indicate below if you want to" echo -e "${InvGreen} ${CClear} enable this feature" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = No)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current: ${CGreen}$exitnodedisp${CClear}" echo "" echo -e "Configure Router as Exit Node?" if promptyn "[y/n]: " then exitnode=1 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Device has been configured as Exit Node." >> $logfile else exitnode=0 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Exit Node configuration has been disabled." >> $logfile fi saveconfig timer=$timerloop if [ "$exitnodedisp" == "No" ] && [ $exitnode -eq 1 ]; then echo "" echo -e "\nChanging exit node configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsup fi fi if [ "$exitnodedisp" == "Yes" ] && [ $exitnode -eq 0 ]; then echo "" echo -e "\nChanging exit node configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsup fi fi } # ------------------------------------------------------------------------------------------------------------------------- # advroutests provide a menu interface to allow for entry of advertised routes advroutests() { clear if [ $advroutes -eq 0 ]; then advroutesdisp="No"; elif [ $advroutes -eq 1 ]; then advroutesdisp="Yes"; fi echo -e "${InvGreen} ${InvDkGray}${CWhite} Advertise Routes on this Router ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Tailscale can act as a 'subnet router' that allow you to access multiple devices" echo -e "${InvGreen} ${CClear} located on your particular subnet through Tailscale. Subnet routers act as a" echo -e "${InvGreen} ${CClear} gateway, relaying traffic from your Tailscale network onto your physical subnet." echo -e "${InvGreen} ${CClear} If you need access to other devices, such as NAS, routers, computers, printers," echo -e "${InvGreen} ${CClear} etc. without the need to install Tailscale software on them, it would be" echo -e "${InvGreen} ${CClear} recommended to enable this feature. Please indicate your choice below." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = Yes)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current: ${CGreen}$advroutesdisp${CClear}" echo -e "${InvGreen} ${CClear} ROUTE(S): ${CGreen}$routes${CClear}" echo "" echo -e "Advertise Routes?" if promptyn "[y/n]: " then echo "" echo "" echo -e "${InvGreen} ${InvDkGray}${CWhite} Advertise Routes on this Router ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate what subnet you want to advertise to your Tailscale network." echo -e "${InvGreen} ${CClear} Typically, you would enter the current subnet of what your router is currently" echo -e "${InvGreen} ${CClear} configured for, ex: 192.168.50.0/24. Should you want to advertise multiple" echo -e "${InvGreen} ${CClear} subnets that are accessible by your router, comma-delimit them in this way:" echo -e "${InvGreen} ${CClear} 192.168.50.0/24,192.168.87.0/24,10.0.100.0/16" echo -e "${InvGreen} ${CClear}" echo -en "${InvGreen} ${CClear} (Default = "; echo -e "$(nvram get lan_ipaddr | cut -d"." -f1-3).0/24)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" read -p "Please enter valid IP4 subnet range? (e=Exit): " routeinput if [ "$routeinput" == "e" ]; then echo -e "\n[Exiting]"; sleep 1 elif [ -z "$routeinput" ]; then advroutes=1 routes=$(nvram get lan_ipaddr | cut -d"." -f1-3).0/24 saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Advertised routes enabled." >> $logfile else advroutes=1 routes=$routeinput saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Advertised routes enabled." >> $logfile fi else advroutes=0 routes="" saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Advertised routes disabled." >> $logfile fi timer=$timerloop if [ "$advroutesdisp" == "No" ] && [ $advroutes -eq 1 ]; then echo "" echo -e "\nChanging exit node configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsup fi fi if [ "$advroutesdisp" == "Yes" ] && [ $advroutes -eq 0 ]; then echo "" echo -e "\nChanging exit node configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsup fi fi } # ------------------------------------------------------------------------------------------------------------------------- # accroutests provide a menu interface to allow for entry to accept linux routes accroutests() { clear if [ $accroutes -eq 0 ]; then accroutesdisp="No"; elif [ $accroutes -eq 1 ]; then accroutesdisp="Yes"; fi echo -e "${InvGreen} ${InvDkGray}${CWhite} Accept Site-to-Site Functionality on this Router ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Clients on Android, iOS, macOS, tvOS, and Windows automatically pick up your new" echo -e "${InvGreen} ${CClear} subnet routes. Only Linux clients using the --accept-routes flag discover the new" echo -e "${InvGreen} ${CClear} routes automatically because the default is to use only the Tailscale IP addresses." echo -e "${InvGreen} ${CClear} This option provides for the basic functionality to allow for site-to-site routing" echo -e "${InvGreen} ${CClear} and communication between networks. Advanced troubleshooting skills may be required" echo -e "${InvGreen} ${CClear} when enabling this option. Please indicate 'y' or 'n' below." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = No)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current: ${CGreen}$accroutesdisp${CClear}" echo "" echo -e "Accept Routes?" if promptyn "[y/n]: " then accroutes=1 saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Accepted Linux routes enabled." >> $logfile else accroutes=0 saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Accepted Linux routes disabled." >> $logfile fi timer=$timerloop if [ "$accroutesdisp" == "No" ] && [ $accroutes -eq 1 ]; then echo "" echo -e "\nChanging routing configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsup sleep 3 fi fi if [ "$accroutesdisp" == "Yes" ] && [ $accroutes -eq 0 ]; then echo "" echo -e "\nChanging routing configuration options will require a restart of Tailscale. Restart now?" if promptyn "[y/n]: " then echo "" echo -e "\n${CGreen}Restarting Tailscale Service and Connection...${CClear}" echo "" tsdown stopts startts tsresetc tsup sleep 3 fi fi } # ------------------------------------------------------------------------------------------------------------------------- # amtmevents lets you pick success or failure amtm email notification selections amtmevents() { while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} AMTM Email Notifications ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate if you would like TAILMON to send you email notifications for${CClear}" echo -e "${InvGreen} ${CClear} Tailscale service/connection failures, or successes, or both? PLEASE NOTE: This${CClear}" echo -e "${InvGreen} ${CClear} does require that AMTM email has been set up successfully under AMTM -> em (email${CClear}" echo -e "${InvGreen} ${CClear} settings). Once you are able to send and receive test emails from AMTM, you may${CClear}" echo -e "${InvGreen} ${CClear} use this functionality in TAILMON. Additionally, this functionality will download${CClear}" echo -e "${InvGreen} ${CClear} an AMTM email interface library courtesey of @Martinsky, and will be located${CClear}" echo -e "${InvGreen} ${CClear} under a new common shared library folder called: /jffs/addons/shared-libs.${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Secondarily, you can choose to rate limit the rate at which emails are sent to${CClear}" echo -e "${InvGreen} ${CClear} your email account per hour. (0=Disabled, 1-9999)${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to enable/disable email event notifications:${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" if [ "$amtmemailsuccess" == "1" ]; then amtmemailsuccessdisp="${CGreen}Y${CCyan}"; else amtmemailsuccess=0; amtmemailsuccessdisp="${CRed}N${CCyan}"; fi if [ "$amtmemailfailure" == "1" ]; then amtmemailfailuredisp="${CGreen}Y${CCyan}"; else amtmemailfailure=0; amtmemailfailuredisp="${CRed}N${CCyan}"; fi if [ "$ratelimit" = "0" ]; then ratelimitdisp="Disabled"; else ratelimitdisp=$ratelimit; fi echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Tailscale Success Event Notifications${CClear} ${CGreen}(1) -${CClear} $amtmemailsuccessdisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Tailscale Failure Event Notifications${CClear} ${CGreen}(2) -${CClear} $amtmemailfailuredisp${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Tailscale Email Rate Limit (per hour)${CClear} ${CGreen}(r) - $ratelimitdisp${CClear}" echo "" read -p "Please select? (1-2, r=Set Email Rate Limit, t=Test Email, e=Exit): " SelectSlot case $SelectSlot in 1) if [ "$amtmemailsuccess" == "0" ]; then amtmemailsuccess=1; amtmemailsuccessdisp="${CGreen}Y${CCyan}"; elif [ "$amtmemailsuccess" == "1" ]; then amtmemailsuccess=0; amtmemailsuccessdisp="${CRed}N${CCyan}"; saveconfig; fi;; 2) if [ "$amtmemailfailure" == "0" ]; then amtmemailfailure=1; amtmemailfailuredisp="${CGreen}Y${CCyan}"; elif [ "$amtmemailfailure" == "1" ]; then amtmemailfailure=0; amtmemailfailuredisp="${CRed}N${CCyan}"; saveconfig; fi;; [Tt]) if [ -f "$CUSTOM_EMAIL_LIBFile" ] then . "$CUSTOM_EMAIL_LIBFile" if [ -z "${CEM_LIB_VERSION:+xSETx}" ] || \ _CheckLibraryUpdates_CEM_ "$CUSTOM_EMAIL_LIBDir" quiet then _DownloadCEMLibraryFile_ "update" fi else _DownloadCEMLibraryFile_ "install" fi cemIsFormatHTML=true cemIsVerboseMode=true ## true OR false ## emailBodyTitle="Testing Email Notification" emailSubject="TEST: TAILMON Email Notification" tmpEMailBodyFile="/tmp/var/tmp/tmpEMailBody_${scriptFileNTag}.$$.TXT" { printf "This is a TEST to check & verify if sending email notifications is working well from TAILMON.\n" } > "$tmpEMailBodyFile" _SendEMailNotification_ "TAILMON v$version" "$emailSubject" "$tmpEMailBodyFile" "$emailBodyTitle" echo "" echo "" read -rsp $'Press any key to acknowledge...\n' -n1 key ;; [Rr]) echo "" read -p "Please enter new Email Rate Limit (per hour)? (0=disabled, 1-9999, e=Exit): " newratelimit if [ "$newratelimit" = "e" ] then echo -e "\n[Exiting]"; sleep 2 elif echo "$newratelimit" | grep -qE "^(0|[1-9][0-9]{0,3})$" && \ [ "$newratelimit" -ge 0 ] && [ "$newratelimit" -le 9999 ] then ratelimit="$newratelimit" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: New Email Rate Limit entered (per hour): $ratelimit" >> $logfile saveconfig else previousValue="$ratelimit" ratelimit="${ratelimit:=0}" [ "$ratelimit" != "$previousValue" ] && \ echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: New Email Rate Limit entered (per hour): $ratelimit" >> $logfile saveconfig fi ;; [Ee]) saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: AMTM Email notification configuration saved" >> $logfile timer=$timerloop break;; esac done } # ------------------------------------------------------------------------------------------------------------------------- ######################################################################## # AMTM Email Notification Functionality generously donated by @Martinski! # # Creation Date: 2020-Jun-11 [Martinski W.] # Last Modified: 2024-Feb-07 [Martinski W.] # Modified for TAILMON Purposes [Viktor Jaep] ######################################################################## #-----------------------------------------------------------# _DownloadCEMLibraryFile_() { local msgStr retCode case "$1" in update) msgStr="Updating" ;; install) msgStr="Installing" ;; *) return 1 ;; esac printf "\33[2K\r" printf "${CGreen}\r[INFO: ${msgStr} the shared AMTM email library script file to support email notifications...]${CClear}" echo -e "$(date +'%b %d %Y %X') $(nvram get lan_hostname) TAILMON[$$] - INFO: ${msgStr} the shared AMTM email library script file to support email notifications..." >> $logfile mkdir -m 755 -p "$CUSTOM_EMAIL_LIBDir" curl -kLSs --retry 3 --retry-delay 5 --retry-connrefused \ "${CEM_LIB_URL}/$CUSTOM_EMAIL_LIBName" -o "$CUSTOM_EMAIL_LIBFile" curlCode="$?" if [ "$curlCode" -eq 0 ] && [ -f "$CUSTOM_EMAIL_LIBFile" ] then retCode=0 chmod 755 "$CUSTOM_EMAIL_LIBFile" . "$CUSTOM_EMAIL_LIBFile" #printf "\nDone.\n" else retCode=1 printf "\33[2K\r" printf "${CRed}\r[ERROR: Unable to download the shared library script file ($CUSTOM_EMAIL_LIBName).]${CClear}" echo -e "$(date +'%b %d %Y %X') $(nvram get lan_hostname) TAILMON[$$] - **ERROR**: Unable to download the shared AMTM email library script file [$CUSTOM_EMAIL_LIBName]." >> $logfile fi return "$retCode" } #-----------------------------------------------------------# # ARG1: The email name/alias to be used as "FROM_NAME" # ARG2: The email Subject string. # ARG3: Full path of file containing the email Body text. # ARG4: The email Body Title string [OPTIONAL]. #-----------------------------------------------------------# _SendEMailNotification_() { if [ -z "${amtmIsEMailConfigFileEnabled:+xSETx}" ] then printf "\33[2K\r" printf "${CRed}\r[ERROR: Email library script ($CUSTOM_EMAIL_LIBFile) *NOT* FOUND.]${CClear}" sleep 5 echo -e "$(date +'%b %d %Y %X') $(nvram get lan_hostname) TAILMON[$$] - **ERROR**: Email library script [$CUSTOM_EMAIL_LIBFile] *NOT* FOUND." >> $logfile return 1 fi if [ $# -lt 3 ] || [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] then printf "\33[2K\r" printf "${CRed}\r[ERROR: INSUFFICIENT email parameters]${CClear}" sleep 5 echo -e "$(date +'%b %d %Y %X') $(nvram get lan_hostname) TAILMON[$$] - **ERROR**: INSUFFICIENT email parameters." >> $logfile return 1 fi local retCode emailBodyTitleStr="" [ $# -gt 3 ] && [ -n "$4" ] && emailBodyTitleStr="$4" FROM_NAME="$1" _SendEMailNotification_CEM_ "$2" "-F=$3" "$emailBodyTitleStr" retCode="$?" if [ "$retCode" -eq 0 ] then printf "\33[2K\r" printf "${CGreen}\r[Email notification was sent successfully ($2)]${CClear}" echo -e "$(date +'%b %d %Y %X') $(nvram get lan_hostname) TAILMON[$$] - INFO: Email notification was sent successfully [$2]" >> $logfile sleep 5 else printf "\33[2K\r" printf "${CRed}\r[ERROR: Failure to send email notification (Error Code: $retCode - $2).]${CClear}" echo -e "$(date +'%b %d %Y %X') $(nvram get lan_hostname) TAILMON[$$] - **ERROR**: Failure to send email notification [$2]" >> $logfile sleep 5 fi return "$retCode" } # ------------------------------------------------------------------------------------------------------------------------- # sendmessage is a function that sends an AMTM email based on activity within TAILMON # $1 = Success/Failure 0/1 # $2 = Component # $3 = VPN Slot sendmessage() { #If AMTM email functionality is disabled, return back to the function call if [ "$amtmemailsuccess" == "0" ] && [ "$amtmemailfailure" == "0" ]; then return fi #Load, install or update the shared AMTM Email integration library if [ -f "$CUSTOM_EMAIL_LIBFile" ] then . "$CUSTOM_EMAIL_LIBFile" if [ -z "${CEM_LIB_VERSION:+xSETx}" ] || \ _CheckLibraryUpdates_CEM_ "$CUSTOM_EMAIL_LIBDir" quiet then _DownloadCEMLibraryFile_ "update" fi else _DownloadCEMLibraryFile_ "install" fi cemIsFormatHTML=true cemIsVerboseMode=false tmpEMailBodyFile="/tmp/var/tmp/tmpEMailBody_${scriptFileNTag}.$$.TXT" ratelimiter emaillimit="$?" if [ "$emaillimit" -eq 0 ] then #Pick the scenario and send email if [ "$1" == "1" ] && [ "$amtmemailfailure" == "1" ]; then if [ "$2" == "Tailscale Service settings out-of-sync" ]; then emailSubject="ALERT: Tailscale Service settings out-of-sync" emailBodyTitle="ALERT: Tailscale Service settings out-of-sync" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "ALERT: TAILMON is currently recovering from out-of-sync settings issues! TAILMON has detected\n" printf "that the Tailscale service settings are not in sync with the TAILMON config. This could be due to a\n" printf "Tailscale update. TAILMON has fixed the settings and restarted the Tailscale service/connection.\n" printf "\n" } > "$tmpEMailBodyFile" elif [ "$2" == "Tailscale Service Restarted" ]; then emailSubject="FAILURE: Tailscale Service Restarted" emailBodyTitle="FAILURE: Tailscale Service Restarted" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "FAILURE: TAILMON has detected that the Tailscale service was dead and not connected. TAILMON.\n" printf "has reset the service, and reestablished a connection to your Tailnet. Please investigate if this\n" printf "behavior continues to persist.\n" printf "\n" } > "$tmpEMailBodyFile" elif [ "$2" == "Router has been restarted" ]; then emailSubject="WARNING: Router Has Unexpectedly Restarted" emailBodyTitle="WARNING: Router Has Unexpectedly Restarted" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "WARNING: TAILMON has detected that the router may have rebooted or was restarted. TAILMON.\n" printf "has reset the service, and reestablished a connection to your Tailnet. Please investigate if this\n" printf "behavior continues to persist.\n" printf "\n" } > "$tmpEMailBodyFile" # Rung: added request email functionality elif [ "$2" == "Tailmon email requested" ]; then emailSubject="WARNING: Router Has Unexpectedly Restarted" emailBodyTitle="WARNING: Router Has Unexpectedly Restarted" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "WARNING: TAILMON has been requested to send this email from the services-start script.\n" printf "If no additional email is received, this means that TAILMON has failed to start for some reason.\n" printf "Please investigate if this behavior continues to persist.\n" printf "\n" } > "$tmpEMailBodyFile" elif [ "$2" == "Unable to reach TAILMON repository" ]; then emailSubject="WARNING: Router unable to reach TAILMON Repository" emailBodyTitle="WARNING: Router unable to reach TAILMON Repository" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "WARNING: TAILMON is unable to reach the TAILMON repository on GitHub in order to perform\n" printf "an autoupdate function. Please check your internet connectivity or any blocking tools in place.\n" printf "Please investigate if this behavior continues to persist.\n" printf "\n" } > "$tmpEMailBodyFile" elif [ "$2" == "Unable to reach Tailscale repository" ]; then emailSubject="WARNING: Router unable to reach Tailscale Repository" emailBodyTitle="WARNING: Router unable to reach Tailscale Repository" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "WARNING: TAILMON is unable to reach the Tailscale repository in order to perform an\n" printf "autoupdate. Please check your internet connectivity or any blocking tools in place.\n" printf "Please investigate if this behavior continues to persist.\n" printf "\n" } > "$tmpEMailBodyFile" fi _SendEMailNotification_ "TAILMON v$version" "$emailSubject" "$tmpEMailBodyFile" "$emailBodyTitle" fi if [ "$1" == "0" ] && [ "$amtmemailsuccess" == "1" ]; then if [ "$2" == "Tailscale Successfully Updated" ]; then emailSubject="SUCCESS: Tailscale Binary was successfully updated via autoupdate" emailBodyTitle="SUCCESS: Tailscale Binary was successfully updated via autoupdate from v$3 to v$4" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "SUCCESS: TAILMON has successfully autoupdated the Tailscale Binary to the latest version.\n" printf "\n" } > "$tmpEMailBodyFile" elif [ "$2" == "TAILMON Script Successfully Updated" ]; then emailSubject="SUCCESS: TAILMON was successfully updated via autoupdate" emailBodyTitle="SUCCESS: TAILMON was successfully updated via autoupdate from v$3 to v$4" { printf "Date/Time: $(date +'%b %d %Y %X')\n" printf "\n" printf "SUCCESS: TAILMON was successfully updated to the latest version via autoupdate.\n" printf "\n" } > "$tmpEMailBodyFile" fi _SendEMailNotification_ "TAILMON v$version" "$emailSubject" "$tmpEMailBodyFile" "$emailBodyTitle" fi fi } # ------------------------------------------------------------------------------------------------------------------------- # Function to keep track of emails sent, and determine if they need to be rate-limited ratelimiter() { #if rate limiting is disabled, exit right away if [ "$ratelimit" = "0" ]; then return 0 fi #Make sure log file exists touch "$tmemails" #check current time and 1h into the past current_time=$(date +%s) cutoff_time=$((current_time - 3600)) #create a temp file where current data will get moved over into that is less than 1hr old tmemailstemp="${tmemails}.tmp" awk -v cutoff="$cutoff_time" '$1 > cutoff' "$tmemails" > "$tmemailstemp" #check to see how many emails have been sent in the last hour recent_email_count=$(wc -l < "$tmemailstemp" | tr -d ' ') printf "\33[2K\r" printf "${CGreen}\r[Checking email rate limit... $recent_email_count/$ratelimit emails sent within the last hour]" sleep 2 #logic to determine if rate limit has been hit if [ "$recent_email_count" -ge "$ratelimit" ] then printf "\33[2K\r" printf "${CGreen}\r[Rate limit exceeded. Emails will be prevented from sending]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Email Rate limit exceeded ($ratelimit). Emails will be prevented from sending." >> $logfile sleep 2 mv "$tmemailstemp" "$tmemails" return 1 else printf "\33[2K\r" printf "${CGreen}\r[Rate within limits. Proceeding to send email]" sleep 1 echo "$current_time" >> "$tmemailstemp" mv "$tmemailstemp" "$tmemails" return 0 fi } # ------------------------------------------------------------------------------------------------------------------------- # installdependencies checks for existence of entware, and if so proceed and install the packages, then run tailmon -config installdependencies() { clear if [ -f "/opt/bin/timeout" ] && [ -f "/opt/sbin/screen" ]; then vconfig else clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Install Dependencies ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Missing dependencies required by TAILMON will be installed during this process." echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "TAILMON has some dependencies in order to function correctly, namely, CoreUtils-Timeout" echo -e "and the Screen utility. These utilities require you to have Entware already installed" echo -e "using the AMTM tool. If Entware is present, the Timeout and Screen utilities will" echo -e "automatically be downloaded and installed during this process." echo "" echo -e "${CGreen}CoreUtils-Timeout${CClear} is a utility that provides more stability for certain routers (like" echo -e "the RT-AC86U) which has a tendency to randomly hang scripts running on this router model." echo "" echo -e "${CGreen}Screen${CClear} is a utility that allows you to run SSH scripts in a standalone environment" echo -e "directly on the router itself, instead of running your commands or a script from a network-" echo -e "attached SSH client. This can provide greater stability due to it running on the router" echo -e "itself." echo "" [ -z "$($timeoutcmd$timeoutsec nvram get odmpid)" ] && RouterModel="$($timeoutcmd$timeoutsec nvram get productid)" || RouterModel="$($timeoutcmd$timeoutsec nvram get odmpid)" # Thanks @thelonelycoder for this logic echo -e "Your router model is: ${CGreen}$RouterModel${CClear}" echo "" echo -e "Ready to install?" if promptyn "[y/n]: " then if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. echo "" echo -e "\n${CClear}Updating Entware Packages..." echo "" opkg update echo "" echo -e "Installing Entware ${CGreen}CoreUtils-Timeout${CClear} Package...${CClear}" echo "" opkg install coreutils-timeout echo "" echo -e "Installing Entware ${CGreen}Screen${CClear} Package...${CClear}" echo "" opkg install screen echo "" echo -e "Install completed..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Entware dependencies installed." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key echo "" echo -e "Executing Configuration Utility..." sleep 1 vconfig else clear echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" echo -e "Please install Entware using the AMTM utility before proceeding..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Entware was not found installed on router. Please investigate." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key exit 1 fi else echo "" echo -e "\n${CClear}[Exiting]" echo "" sleep 1 exit 0 fi fi } # ------------------------------------------------------------------------------------------------------------------------- # reinstalldependencies force re-installs the entware packages reinstalldependencies() { clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Re-install Dependencies ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Missing dependencies required by TAILMON will be re-installed during this process." echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "Would you like to re-install the CoreUtils-Timeout and the Screen utility? These" echo -e "utilities require you to have Entware already installed using the AMTM tool. If Entware" echo -e "is present, the Timeout and Screen utilities will be uninstalled, downloaded and re-" echo -e "installed during this setup process..." echo "" echo -e "${CGreen}CoreUtils-Timeout${CClear} is a utility that provides more stability for certain routers (like" echo -e "the RT-AC86U) which has a tendency to randomly hang scripts running on this router" echo -e "model." echo "" echo -e "${CGreen}Screen${CClear} is a utility that allows you to run SSH scripts in a standalone environment" echo -e "directly on the router itself, instead of running your commands or a script from a" echo -e "network-attached SSH client. This can provide greater stability due to it running on" echo -e "the router itself." echo "" [ -z "$($timeoutcmd$timeoutsec nvram get odmpid)" ] && RouterModel="$($timeoutcmd$timeoutsec nvram get productid)" || RouterModel="$($timeoutcmd$timeoutsec nvram get odmpid)" # Thanks @thelonelycoder for this logic echo -e "Your router model is: ${CGreen}$RouterModel${CClear}" echo "" echo -e "Force Re-install?" if promptyn "[y/n]: " then if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. echo "" echo -e "\nUpdating Entware Packages..." echo "" opkg update echo "" echo -e "Force Re-installing Entware ${CGreen}CoreUtils-Timeout${CClear} Package..." echo "" opkg install --force-reinstall coreutils-timeout echo "" echo -e "Force Re-installing Entware ${CGreen}Screen${CClear} Package..." echo "" opkg install --force-reinstall screen echo "" echo -e "Re-install completed..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Entware dependencies re-installed." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key else clear echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" echo -e "Please install Entware using the AMTM utility before proceeding..." echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Entware was not found installed on router. Please investigate." >> $logfile echo "" read -rsp $'Press any key to continue...\n' -n1 key exit 1 fi fi } # ------------------------------------------------------------------------------------------------------------------------- # vsetup provide a menu interface to allow for initial component installs, uninstall, etc. vsetup() { if [ ! -f "/opt/bin/timeout" ] || [ ! -f "/opt/sbin/screen" ]; then installdependencies fi while true; do clear # Initial Setup if [ -f $config ]; then source $config else saveconfig fi if [ -f "/opt/bin/tailscale" ]; then tsinstalleddisp="Installed"; else tsinstalleddisp="Not Installed"; fi if [ $exitnode -eq 0 ]; then exitnodedisp="No"; elif [ $exitnode -eq 1 ]; then exitnodedisp="Yes"; fi if [ $advroutes -eq 0 ]; then advroutesdisp="No"; elif [ $advroutes -eq 1 ]; then advroutesdisp="Yes ($routes)"; fi if [ $accroutes -eq 0 ]; then accroutesdisp="No"; elif [ $accroutes -eq 1 ]; then accroutesdisp="Yes"; fi tsver=$(tailscale version | awk 'NR==1 {print $1}') >/dev/null 2>&1 if [ -z "$tsver" ]; then tsver="0.00"; fi echo -e "${InvGreen} ${InvDkGray}${CWhite} TAILMON Main Setup and Configuration Menu ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please choose from the various options below, which allow you to perform high level${CClear}" echo -e "${InvGreen} ${CClear} actions in the management of the TAILMON script.${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 1)${CClear} : Install Tailscale Entware Package(s) : ${CGreen}$tsinstalleddisp${CClear}" printf "\33[2K\r" printf "${CGreen}\r[Checking Services...Stand By]" /opt/etc/init.d/S06tailscaled check >/dev/null 2>&1 tsservice=$? if [ $tsservice -ne 0 ]; then tsservicedisp="Stopped"; else tsservicedisp="Started"; fi tailscale status >/dev/null 2>&1 tsconn=$? if [ $tsconn -ne 0 ]; then tsconndisp="Disconnected"; else tsconndisp="Connected"; fi printf "\33[2K\r" if [ "$tsinstalleddisp" == "Installed" ]; then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- ${CGreen}(R)${CClear}e-${CGreen}(S)${CClear}tart / S${CGreen}(T)${CClear}op Tailscale Service${CClear} |--- ${CGreen}$tsservicedisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- ${CGreen}(U)${CClear}p / ${CGreen}(D)${CClear}own Tailscale Connection${CClear} |--- ${CGreen}$tsconndisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- U${CGreen}(P)${CClear}date Tailscale Binary to latest version |--- ${CGreen}v$tsver${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- Update Tailscale Binary to latest ${CGreen}(B)${CRed}ETA${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- ${CGreen}(F)${CClear}orce Downgrade to Older Tailscale version${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- ${CGreen}(I)${CClear}ssue Connection '--reset' Command${CClear}" fi echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 2)${CClear} : Uninstall Tailscale Entware Package(s)${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 3)${CClear} : Set Tailscale Operating Mode : ${CGreen}$tsoperatingmode${CClear}" if [ "$tsoperatingmode" == "Custom" ]; then echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} |-${CClear}-- Edit Custom ${InvGreen}${CWhite}(O)${CClear}peration Mode Settings${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 4)${CClear}${CDkGray} : Configure this Router as Exit Node : $exitnodedisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear}${CDkGray} : Advertise Routes on this router : $advroutesdisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 6)${CClear}${CDkGray} : Enable Site-to-Site functionality on router : $accroutesdisp${CClear}" else echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 4)${CClear} : Configure this Router as Exit Node : ${CGreen}$exitnodedisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear} : Advertise Routes on this router : ${CGreen}$advroutesdisp${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 6)${CClear} : Enable Site-to-Site functionality on router : ${CGreen}$accroutesdisp${CClear}" fi echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 7)${CClear} : Custom configuration options for TAILMON${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 8)${CClear} : Force reinstall Entware dependencies${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 9)${CClear} : Check for latest updates${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(10)${CClear} : Uninstall TAILMON${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} | ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( L)${CClear} : Launch TAILMON in Monitoring Mode (${CGreen}sh /jffs/scripts/tailmon.sh${CClear})" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( M)${CClear} : Launch TAILMON in Monitoring Mode using SCREEN (${CGreen}sh /jf..ts/tailmon.sh -screen${CClear})" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} | ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( e)${CClear} : Exit${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" if [ "$tsinstalleddisp" == "Installed" ]; then if [ "$tsoperatingmode" == "Custom" ]; then read -p "Please select? (1-10, R/S/T/U/D/P/B/F/I/O/L/M, e=Exit): " SelectSlot else read -p "Please select? (1-10, R/S/T/U/D/P/B/F/I/L/M, e=Exit): " SelectSlot fi else read -p "Please select? (1-10, L/M, e=Exit): " SelectSlot fi case $SelectSlot in [Rr]) echo ""; restarttsc;; [Ss]) echo ""; startts;; [Tt]) echo ""; stopts;; [Uu]) echo ""; tsup;; [Dd]) echo ""; tsdown;; [Ll]) exec sh /jffs/scripts/tailmon.sh -noswitch;; [Mm]) exec sh /jffs/scripts/tailmon.sh -screen -now;; [Oo]) if [ "$tsoperatingmode" == "Custom" ]; then customconfig fi ;; [Pp]) echo ""; tsupdate;; [Bb]) echo ""; tsbeta;; [Ff]) echo ""; tsdowngrade;; [Ii]) echo ""; tsreset;; 1) installts;; 2) uninstallts;; 3) if [ -f "/opt/bin/tailscale" ]; then operatingmode; fi;; 4) if [ "$tsoperatingmode" != "Custom" ]; then if [ -f "/opt/bin/tailscale" ]; then exitnodets; fi fi ;; 5) if [ "$tsoperatingmode" != "Custom" ]; then if [ -f "/opt/bin/tailscale" ]; then advroutests; fi fi ;; 6) if [ "$tsoperatingmode" != "Custom" ]; then if [ -f "/opt/bin/tailscale" ]; then accroutests; fi fi ;; 7) installdependencies;; 8) reinstalldependencies;; 9) vupdate;; 10) vuninstall;; [Ee]) echo ""; timer=$timerloop; break;; esac done } # ------------------------------------------------------------------------------------------------------------------------- # vconfig is a function that provides a UI to choose various options for tailmon vconfig() { # Grab the TAILMON config file and read it in if [ -f $config ]; then source $config else initialsetup fi while true; do if [ $keepalive -eq 0 ]; then keepalivedisp="No" else keepalivedisp="Yes" fi if [ $persistentsettings -eq 0 ]; then persistentsettingsdisp="No" else persistentsettingsdisp="Yes" fi if [ "$amtmemailsuccess" == "0" ] && [ "$amtmemailfailure" == "0" ]; then amtmemailsuccfaildisp="Disabled" elif [ "$amtmemailsuccess" == "1" ] && [ "$amtmemailfailure" == "0" ]; then amtmemailsuccfaildisp="Success" elif [ "$amtmemailsuccess" == "0" ] && [ "$amtmemailfailure" == "1" ]; then amtmemailsuccfaildisp="Failure" elif [ "$amtmemailsuccess" == "1" ] && [ "$amtmemailfailure" == "1" ]; then amtmemailsuccfaildisp="Success, Failure" else amtmemailsuccfaildisp="Disabled" fi rldisp="" if [ "$amtmemailsuccess" = "1" ] || [ "$amtmemailfailure" = "1" ] then if [ "$ratelimit" = "0" ]; then rldisp="| ${CRed}RL" else rldisp="| ${CGreen}RL:$ratelimit/h" fi fi if [ $autostart -eq 0 ]; then autostartdisp="Disabled" elif [ $autostart -eq 1 ]; then autostartdisp="Enabled" fi #scheduler colors and indicators if [ "$schedule" = "0" ] then schedtime="${CDkGray}01:00${CClear}" elif [ "$schedule" = "1" ] then schedhrs="$(printf "%02d" "$schedulehrs")" schedmin="$(printf "%02d" "$schedulemin")" schedtime="${CGreen}$schedhrs:$schedmin${CClear}" fi clear echo -e "${InvGreen} ${InvDkGray}${CWhite} TAILMON Configuration Option ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please choose from the various options below, which allow you to modify certain${CClear}" echo -e "${InvGreen} ${CClear} customizable parameters that affect the operation of this script.${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(1)${CClear} : Keep Tailscale Service Alive : ${CGreen}$keepalivedisp" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(2)${CClear} : Timer Check Loop Interval : ${CGreen}${timerloop}sec" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(3)${CClear} : Custom Event Log size (rows) : ${CGreen}$logsize" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(4)${CClear} : AMTM Email Notifications / Rate Limiting : ${CGreen}$amtmemailsuccfaildisp $rldisp" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(5)${CClear} : Keep settings on Tailscale Entware updates : ${CGreen}$persistentsettingsdisp" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(6)${CClear} : Autostart TAILMON on Reboot : ${CGreen}$autostartdisp" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(7)${CClear} : Schedule TAILMON + Tailscale Autoupdate : ${CGreen}$schedtime${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} | ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(e)${CClear} : Exit${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" read -p "Please select? (1-7, e=Exit): " SelectSlot case $SelectSlot in 1) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Keep Tailscale Service Alive ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate if you want TAILMON to check the status of the Tailscale Service${CClear}" echo -e "${InvGreen} ${CClear} and restart it if necessary? While Tailscale overall is fairly stable, there are${CClear}" echo -e "${InvGreen} ${CClear} instances where the service with terminate." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = Yes)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "${CClear}Current: ${CGreen}$keepalivedisp${CClear}" echo "" echo -e "Keep Alive?" if promptyn "[y/n]: " then keepalive=1 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON keepalive enabled." >> $logfile else keepalive=0 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON keepalive disabled." >> $logfile fi saveconfig ;; 2) timerloopconfig ;; 3) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Custom Event Log Size ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate below how large you would like your Event Log to grow. I'm a poet${CClear}" echo -e "${InvGreen} ${CClear} and didn't even know it. By default, with 2000 rows, you will have many months of${CClear}" echo -e "${InvGreen} ${CClear} Event Log data." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Use 0 to Disable, max number of rows is 9999. (Default = 2000)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "${CClear}Current: ${CGreen}$logsize${CClear}" echo "" read -p "Please enter Log Size (in rows)? (0-9999, e=Exit): " NEWLOGSIZE if [ "$NEWLOGSIZE" == "e" ]; then echo -e "\n[Exiting]"; sleep 1 elif [ $NEWLOGSIZE -ge 0 ] && [ $NEWLOGSIZE -le 9999 ]; then logsize=$NEWLOGSIZE saveconfig echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Event log size configured for $logsize rows." >> $logfile else logsize=2000 saveconfig fi ;; 4) amtmevents source $config ;; 5) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Keep Settings Persistent on Tailscale Entware Updates ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Please indicate if you want TAILMON to check the Tailscale Service settings on${CClear}" echo -e "${InvGreen} ${CClear} a regular basis to determine if settings are out-of-sync due to a possible${CClear}" echo -e "${InvGreen} ${CClear} Tailscale Entware upgrade? A common side-effect after updating the Tailscale${CClear}" echo -e "${InvGreen} ${CClear} Entware package is that it will remove your previously configured settings,${CClear}" echo -e "${InvGreen} ${CClear} which could cause your router to no longer participate on your tailnet.${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = No)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "${CClear}Current: ${CGreen}$persistentsettingsdisp${CClear}" echo "" echo -e "Keep Settings Persistent?" if promptyn "[y/n]: " then persistentsettings=1 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON Keep Settings Persistent enabled." >> $logfile else persistentsettings=0 echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON Keep Settings Persistent disabled." >> $logfile fi saveconfig ;; 6) autostart;; 7) scheduleautoupdates;; [Ee]) echo -e "${CClear}\n[Exiting]"; sleep 1; resettimer=1; break ;; esac done } # ------------------------------------------------------------------------------------------------------------------------- # vupdate is a function that provides a UI to check for script updates and allows you to install the latest version... vupdate() { while true; do updatecheck # Check for the latest stable version from source repository betacheck # Check for the latest beta version from source repository if [ "$track" = "0" ]; then trackdisp="Stable" else trackdisp="Beta" fi clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Update Utility ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} This utility allows you to check, download and install updates from your preferred" echo -e "${InvGreen} ${CClear} Beta/Stable track subscription." echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} (Default = 0 - Stable)" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CWhite} Stable Track${CClear}" echo -e "${InvGreen} ${CClear} Local Version: ${CGreen}$version${CClear}" echo -e "${InvGreen} ${CClear} Official Version: ${CGreen}$DLversion${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CWhite} Beta Track" echo -e "${InvGreen} ${CClear} Local Version: ${CGreen}$version${CClear}" echo -e "${InvGreen} ${CClear} Latest Beta Version: ${CGreen}$Bversion${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Your subscribed track: ${CGreen}$trackdisp${CClear}" echo -e "${InvGreen} ${CClear}" echo "" if [ "$track" = "0" ]; then if [ "$version" == "$DLversion" ] then echo -e "You are on the latest ${CGreen}STABLE${CClear} version! Download & Overwrite, or change Tracks?${CClear}" read -p "(Stable = 0, Beta = 1, Download = y/n, e=Exit): " SelectUpdate case $SelectUpdate in 0) track=0 saveconfig ;; 1) track=1 saveconfig ;; [Yy]) echo "" echo -e "\nDownloading TAILMON ${CGreen}STABLE${CClear}" curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/main/tailmon.sh" -o "/jffs/scripts/tailmon.sh" && chmod 755 "/jffs/scripts/tailmon.sh" echo "" echo -e "Download successful!${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON update successfully downloaded and installed." >> $logfile echo "" read -rsp $'Press any key to restart TAILMON...\n' -n1 key exec /jffs/scripts/tailmon.sh -setup ;; [Nn]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; [Ee]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; esac else echo -e "New ${CGreen}STABLE${CClear} version available! Download & Upgrade, or change Tracks?${CClear}" read -p "(Stable = 0, Beta = 1, Download = y/n, e=Exit): " SelectUpdate case $SelectUpdate in 0) track=0 saveconfig ;; 1) track=1 saveconfig ;; [Yy]) echo "" echo -e "\nDownloading TAILMON ${CGreen}STABLE${CClear}" curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/main/tailmon.sh" -o "/jffs/scripts/tailmon.sh" && chmod 755 "/jffs/scripts/tailmon.sh" echo "" echo -e "Download successful!${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON update successfully downloaded and installed." >> $logfile echo "" read -rsp $'Press any key to restart TAILMON...\n' -n1 key exec /jffs/scripts/tailmon.sh -setup ;; [Nn]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; [Ee]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; esac fi elif [ "$track" = "1" ]; then if [ "$version" == "$Bversion" ]; then echo -e "You are on the latest ${CGreen}BETA${CClear} version! Download & Overwrite, or change Tracks?${CClear}" read -p "(Stable = 0, Beta = 1, Download = y/n, e=Exit): " SelectUpdate case $SelectUpdate in 0) track=0 saveconfig ;; 1) track=1 saveconfig ;; [Yy]) echo "" echo -e "\nDownloading TAILMON ${CGreen}BETA${CClear}" curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/develop/tailmon.sh" -o "/jffs/scripts/tailmon.sh" && chmod 755 "/jffs/scripts/tailmon.sh" echo "" echo -e "Download successful!${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON BETA update successfully downloaded and installed." >> $logfile echo "" read -rsp $'Press any key to restart TAILMON...\n' -n1 key exec /jffs/scripts/tailmon.sh -setup ;; [Nn]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; [Ee]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; esac else echo -e "New ${CGreen}BETA${CClear} version available! Download & Upgrade, or change Tracks?${CClear}" read -p "(Stable = 0, Beta = 1, Download = y/n, e=Exit): " SelectUpdate case $SelectUpdate in 0) track=0 saveconfig ;; 1) track=1 saveconfig ;; [Yy]) echo "" echo -e "\nDownloading TAILMON ${CGreen}BETA${CClear}" curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/develop/tailmon.sh" -o "/jffs/scripts/tailmon.sh" && chmod 755 "/jffs/scripts/tailmon.sh" echo "" echo -e "Download successful!${CClear}" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON BETA update successfully downloaded and installed." >> $logfile echo "" read -rsp $'Press any key to restart TAILMON...\n' -n1 key exec /jffs/scripts/tailmon.sh -setup ;; [Nn]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; [Ee]) echo "" echo "" echo -e "${CClear}Exiting Update Utility..." sleep 1 return ;; esac fi fi done } # ------------------------------------------------------------------------------------------------------------------------- # updatecheck is a function that downloads the latest update version file, and compares it with what's currently installed updatecheck() { # Download the latest version file from the source repository curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/main/version.txt" -o "/jffs/addons/tailmon.d/version.txt" if [ -f $dlverpath ] then # Read in its contents for the current version file DLversion=$(cat $dlverpath) # Compare the new version with the old version and log it if [ "$beta" == "1" ]; then # Check if Dev/Beta Mode is enabled and disable notification message UpdateNotify=0 elif [ "$DLversion" != "$version" ]; then DLversionPF=$(printf "%-8s" $DLversion) versionPF=$(printf "%-8s" $version) UpdateNotify="${InvYellow} ${InvDkGray}${CWhite} Stable Track Update available: v$versionPF -> v$DLversionPF ${CClear}" else UpdateNotify=0 fi fi } # ------------------------------------------------------------------------------------------------------------------------- # betacheck is a function that downloads the latest beta version file, and compares it with what's currently installed betacheck() { # Download the latest version file from the source repository curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 1 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/TAILMON/develop/version.txt" -o "/jffs/addons/tailmon.d/beta.txt" if [ -f $bverpath ] then # Read in its contents for the current version file Bversion=$(cat $bverpath) # Compare the new version with the old version and log it if [ "$beta" == "1" ]; then # Check if Dev/Beta Mode is enabled and disable notification message BUpdateNotify=0 elif [ "$Bversion" != "$version" ]; then BversionPF=$(printf "%-8s" $Bversion) versionPF=$(printf "%-8s" $version) BUpdateNotify="${InvYellow} ${InvDkGray}${CWhite} Beta Track Update available: v$versionPF -> v$BversionPF ${CClear}" else BUpdateNotify=0 fi fi } # ------------------------------------------------------------------------------------------------------------------------- # vuninstall is a function that uninstalls and removes all traces of tailmon/tailscale from your router... vuninstall() { while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Uninstall Utility ${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} You are about to uninstall TAILMON and optionally, Tailscale from your router! This" echo -e "${InvGreen} ${CClear} action is irreversible." echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" echo -e "Do you wish to proceed?${CClear}" if promptyn "[y/n]: "; then echo "" echo -e "\nAre you sure? Please type 'y' to validate you wish to proceed.${CClear}" if promptyn "[y/n]: "; then #Remove and uninstall files/directories rm -f -r /jffs/addons/tailmon.d >/dev/null 2>&1 rm -f /jffs/scripts/tailmon.sh >/dev/null 2>&1 sed -i -e '/tailmon.sh/d' /jffs/scripts/post-mount >/dev/null 2>&1 echo "" echo -e "\n${CGreen}TAILMON has been uninstalled...${CClear}" echo "" if [ -f "/opt/bin/tailscale" ]; then echo -e "Would you also like to uninstall Tailscale from your router?" if promptyn "[y/n]: "; then if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. echo "" echo -e "\n${CGreen}Shutting down Tailscale...${CClear}" tailscale logout tailscale down /opt/etc/init.d/S06tailscaled stop echo "" echo -e "\n${CGreen}Removing firewall-start entries...${CClear}" #remove firewall-start entry if found if [ -f /jffs/scripts/firewall-start ]; then if grep -q -F "if [ -x /opt/bin/tailscale ]; then tailscale down; tailscale up; fi" /jffs/scripts/firewall-start; then sed -i -e '/tailscale down/d' /jffs/scripts/firewall-start fi fi echo -e "\n${CGreen}Updating Entware Packages...${CClear}" echo "" opkg update echo "" echo -e "${CGreen}Uninstalling Entware Tailscale Package(s)...${CClear}" echo "" archker=$(opkg print-architecture | grep "armv7-2.6") if [ -z "$archker" ]; then opkg remove tailscale else opkg remove tailscale_nohf #remove special tailscale package for arm7 kernel 2.6 fi rm -f /opt/var/tailscaled.state >/dev/null 2>&1 rm -r /opt/var/lib/tailscale >/dev/null 2>&1 rm -r /opt/var/run/tailscale >/dev/null 2>&1 rm -r /var/run/tailscale >/dev/null 2>&1 rm -r /var/lib/tailscale >/dev/null 2>&1 echo "" read -rsp $'Press any key to continue...\n' -n1 key echo "" echo -e "${CClear}" exit 0 break else clear echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" echo -e "Please install Entware using the AMTM utility before proceeding..." echo "" read -rsp $'Press any key to continue...\n' -n1 key exit 1 fi exit 0 else echo "" echo -e "\nExiting Uninstall Utility...${CClear}" sleep 1 echo "" echo -e "${CClear}" exit 0 fi fi exit 0 else echo "" echo -e "\nExiting Uninstall Utility...${CClear}" sleep 1 return fi fi done } # ------------------------------------------------------------------------------------------------------------------------- # vlogs is a function that calls the nano text editor to view the TAILMON log file vlogs() { export TERM=linux nano +999999 --linenumbers $logfile timer=$timerloop trimlogs } # ------------------------------------------------------------------------------------------------------------------------- # trimlogs will cut down log size (in rows) based on custom value trimlogs() { if [ $logsize -gt 0 ]; then currlogsize=$(wc -l $logfile | awk '{ print $1 }' ) # Determine the number of rows in the log if [ $currlogsize -gt $logsize ] # If it's bigger than the max allowed, tail/trim it! then echo "$(tail -$logsize $logfile)" > $logfile fi fi } # ------------------------------------------------------------------------------------------------------------------------- # saveconfig saves the tailmon.cfg file after every major change, and applies that to the script on the fly saveconfig() { { echo 'track='$track echo 'keepalive='$keepalive echo 'timerloop='$timerloop echo 'logsize='$logsize echo 'autostart='$autostart echo 'schedule='$schedule echo 'schedulehrs='$schedulehrs echo 'schedulemin='$schedulemin echo 'updatetm='$updatetm echo 'updatets='$updatets echo 'amtmemailsuccess='$amtmemailsuccess echo 'amtmemailfailure='$amtmemailfailure echo 'ratelimit='$ratelimit echo 'tsoperatingmode="'"$tsoperatingmode"'"' echo 'persistentsettings='$persistentsettings echo 'exitnode='$exitnode echo 'advroutes='$advroutes echo 'accroutes='$accroutes echo 'precmd="'"$precmd"'"' echo 'args="'"$args"'"' echo 'preargs="'"$preargs"'"' echo 'routes="'"$routes"'"' echo 'customcmdline="'"$customcmdline"'"' } > $config echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: TAILMON config has been updated." >> $logfile if [ -f $config ]; then source $config fi } # ------------------------------------------------------------------------------------------------------------------------- # Begin main commandline switch logic # ------------------------------------------------------------------------------------------------------------------------- # Remove Maintenance Mode file lock rm -f /jffs/addons/tailmon.d/updating.txt >/dev/null 2>&1 # Check for updates updatecheck betacheck # Check and see if any commandline option is being used if [ $# -eq 0 ] then clear exec sh /jffs/scripts/tailmon.sh -noswitch exit 0 fi # Check and see if an invalid commandline option is being used # Rung: adding email switch if [ "$1" == "-h" ] || [ "$1" == "-help" ] || [ "$1" == "-setup" ] || [ "$1" == "-bw" ] || [ "$1" == "-noswitch" ] || [ "$1" == "-screen" ] || [ "$1" == "-now" ] || [ "$1" == "-email" ] || [ "$1" == "-autoupdate" ] then clear else clear echo "" echo "TAILMON v$version" echo "" echo "Exiting due to invalid commandline options!" echo "(run 'tailmon.sh -h' for help)" echo "" echo -e "${CClear}" exit 0 fi # Check to see if the help option is being called if [ "$1" == "-h" ] || [ "$1" == "-help" ] then clear echo "" echo "TAILMON v$version Commandline Option Usage:" echo "" echo "tailmon -h | -help" echo "tailmon -setup" echo "tailmon -bw" echo "tailmon -screen" echo "tailmon -screen -now" echo "" echo " -h | -help (this output)" echo " -setup (displays the setup menu)" echo " -bw (runs tailmon in monochrome mode)" echo " -screen (runs tailmon in screen background)" echo " -screen -now (runs tailmon in screen background immediately)" echo "" echo -e "${CClear}" exit 0 fi # Rung: added email switch if [ "$1" == "-email" ] then amtmemailfailure=1 sendmessage 1 "Tailmon email requested" exit 0 fi # Check to see if autoupdate is being called if [ "$1" == "-autoupdate" ] then # Grab the TAILMON config file and read it in if [ -f $config ]; then source $config else initialsetup fi autoupdate exit 0 fi # Check to see if a second command is being passed to remove color if [ "$1" == "-bw" ] || [ "$2" == "-bw" ] then blackwhite fi # Check to see if the -now parameter is being called to bypass the screen timer if [ "$2" == "-now" ] then bypassscreentimer=1 fi # Check to see if the setup option is being called if [ "$1" == "-setup" ] then # Create the necessary folder/file structure for tailmon under /jffs/addons if [ ! -d "/jffs/addons/tailmon.d" ]; then mkdir -p "/jffs/addons/tailmon.d" fi logoNM vsetup exit 0 fi # Check to see if the screen option is being called and run operations normally using the screen utility if [ "$1" == "-screen" ] then screen -wipe >/dev/null 2>&1 # Kill any dead screen sessions sleep 1 ScreenSess=$(screen -ls | grep "tailmon" | awk '{print $1}' | cut -d . -f 1) if [ -z $ScreenSess ]; then if [ "$bypassscreentimer" == "1" ]; then screen -dmS "tailmon" $apppath -noswitch sleep 1 screen -r tailmon else clear echo -e "${CClear}Executing ${CGreen}TAILMON v$version${CClear} using the SCREEN utility..." echo "" echo -e "${CClear}IMPORTANT:" echo -e "${CClear}In order to keep TAILMON running in the background," echo -e "${CClear}properly exit the SCREEN session by using: ${CGreen}CTRL-A + D${CClear}" echo "" screen -dmS "tailmon" $apppath -noswitch sleep 5 screen -r tailmon exit 0 fi else if [ "$bypassscreentimer" == "1" ]; then sleep 1 else clear echo -e "${CClear}Connecting to existing ${CGreen}TAILMON v$version${CClear} SCREEN session...${CClear}" echo "" echo -e "${CClear}IMPORTANT:${CClear}" echo -e "${CClear}In order to keep TAILMON running in the background,${CClear}" echo -e "${CClear}properly exit the SCREEN session by using: ${CGreen}CTRL-A + D${CClear}" echo "" echo -e "${CClear}Switching to the SCREEN session in T-5 sec...${CClear}" echo -e "${CClear}" spinner 5 fi fi screen -dr $ScreenSess exit 0 fi # Check to see if the noswitch option is being called if [ "$1" == "-noswitch" ] then clear #last switch before the main program starts if [ ! -f $cfgpath ]; then initialsetup else source $config fi #Display TAILMON Update Log Notifications if [ "$track" = "0" ]; then if [ "$UpdateNotify" != "0" ] then echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: New TAILMON STABLE TRACK v$DLversion available for download/install." >> $logfile fi fi if [ "$track" = "1" ]; then if [ "$BUpdateNotify" != "0" ] then echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: New TAILMON BETA TRACK v$Bversion available for download/install." >> $logfile fi fi fi # ------------------------------------------------------------------------------------------------------------------------- # Begin TAILMON Main Loop # ------------------------------------------------------------------------------------------------------------------------- #DEBUG=; set -x # uncomment/comment to enable/disable debug mode #{ # uncomment/comment to enable/disable debug mode # Create the necessary folder/file structure for tailmon under /jffs/addons if [ ! -d "/jffs/addons/tailmon.d" ]; then mkdir -p "/jffs/addons/tailmon.d" fi # Check for and add an alias for TAILMON if ! grep -F "sh /jffs/scripts/tailmon.sh" /jffs/configs/profile.add >/dev/null 2>/dev/null; then echo "alias tailmon=\"sh /jffs/scripts/tailmon.sh\" # added by tailmon" >> /jffs/configs/profile.add fi if [ ! -f "/opt/bin/timeout" ] || [ ! -f "/opt/sbin/screen" ]; then installdependencies fi if [ -f "/opt/bin/timeout" ] # If the timeout utility is available then use it and assign variables then timeoutcmd="timeout " timeoutsec="10" timeoutlng="60" else timeoutcmd="" timeoutsec="" timeoutlng="" fi while true; do clear # Grab the TAILMON config file and read it in if [ -f $config ]; then source $config else initialsetup fi while [ -f /jffs/addons/tailmon.d/updating.txt ]; do clear echo -e "${CGreen}[TAILMON is in Maintenance Mode]${CClear}" echo "" echo -e "Trying again in 30 seconds..." echo "" spinner 30 done if [ -f "/opt/bin/tailscale" ]; then tsinstalled=1 if [ $keepalive -eq 1 ]; then keepalivedisp="Yes" else keepalivedisp="No" fi if [ "$amtmemailsuccess" == "0" ] && [ "$amtmemailfailure" == "0" ]; then amtmdisp="${CDkGray}Disabled " elif [ "$amtmemailsuccess" == "1" ] && [ "$amtmemailfailure" == "0" ]; then amtmdisp="${CGreen}Success " elif [ "$amtmemailsuccess" == "0" ] && [ "$amtmemailfailure" == "1" ]; then amtmdisp="${CGreen}Failure " elif [ "$amtmemailsuccess" == "1" ] && [ "$amtmemailfailure" == "1" ]; then amtmdisp="${CGreen}Success, Failure" else amtmdisp="${CDkGray}Disabled " fi rldisp="" if [ "$amtmemailsuccess" = "1" ] || [ "$amtmemailfailure" = "1" ] then if [ "$ratelimit" = "0" ]; then rldisp="| ${CRed}RL" else rldisp="| ${CClear}RL: ${CGreen}$ratelimit/h" fi fi tzone=$(date +%Z) tzonechars=$(echo ${#tzone}) if [ $tzonechars = 1 ]; then tzspaces=" "; elif [ $tzonechars = 2 ]; then tzspaces=" "; elif [ $tzonechars = 3 ]; then tzspaces=" "; elif [ $tzonechars = 4 ]; then tzspaces=" "; elif [ $tzonechars = 5 ]; then tzspaces=" "; fi tsver=$(tailscale version | awk 'NR==1 {print $1}') >/dev/null 2>&1 if [ -z "$tsver" ]; then tsver="0.00"; fi #Display tailmon Update Notifications if [ "$track" = "0" ]; then if [ "$UpdateNotify" != "0" ] then echo -e "$UpdateNotify" fi fi if [ "$track" = "1" ]; then if [ "$BUpdateNotify" != "0" ] then echo -e "$BUpdateNotify" fi fi #Display tailmon client header echo -en "${InvGreen} ${InvDkGray} TAILMON - v" printf "%-8s" $version echo -e " ${CWhite}Operations Menu ${InvDkGray} $tzspaces$(date) ${CClear}" echo -e "${InvGreen} ${CClear} ${CGreen}(R)${CClear}e-${CGreen}(S)${CClear}tart / S${CGreen}(T)${CClear}op Tailscale Service ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu $rldisp${CClear}" echo -e "${InvGreen} ${CClear} Tailscale Connection ${CGreen}(U)${CClear}p / ${CGreen}(D)${CClear}own ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): ${CGreen}$logsize${CClear}" if [ "$tsoperatingmode" == "Custom" ]; then echo -e "${InvGreen} ${CClear} Custom ${CGreen}(O)${CClear}peration Mode Settings ${InvGreen} ${CClear} ${CGreen}(K)${CClear}eep Tailscale Service Alive: ${CGreen}$keepalivedisp${CClear}" else echo -e "${InvGreen} ${CClear} ${CDkGray}Custom (O)peration Mode Settings${CClear} ${InvGreen} ${CClear} ${CGreen}(K)${CClear}eep Tailscale Service Alive: ${CGreen}$keepalivedisp${CClear}" fi echo -e "${InvGreen} ${CClear} ${CGreen}(A)${CClear}MTM Email Notifications: $amtmdisp ${InvGreen} ${CClear} Ti${CGreen}(M)${CClear}er Check Loop Interval: ${CGreen}${timerloop}sec${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}--------------------------------------------------------------------------------------------------------------${CClear}" echo "" echo -en "${InvDkGray}${CWhite}Tailscale Service v" printf "%-8s" $tsver echo -e " ${CClear}" /opt/etc/init.d/S06tailscaled check tsservice=$? echo "" echo -e "${InvDkGray}${CWhite}Tailscale Connection Status: ${CClear}" tailscale status tsstatus=$? echo "" if [ "$tsoperatingmode" == "Userspace" ]; then echo -e "${InvDkGray}${CWhite}Tailscale Service Options (Userspace Mode) ${CClear}" echo -e "${CWhite}ARGS: ${CGreen}$args" echo -e "${CWhite}PREARGS: ${CGreen}$preargs" elif [ "$tsoperatingmode" == "Kernel" ]; then echo -e "${InvDkGray}${CWhite}Tailscale Service Options (Kernel Mode) ${CClear}" echo -e "${CWhite}PRECMD: ${CGreen}$precmd" echo -e "${CWhite}ARGS: ${CGreen}$args" echo -e "${CWhite}PREARGS: ${CGreen}$preargs" elif [ "$tsoperatingmode" == "Custom" ]; then echo -e "${InvDkGray}${CWhite}Tailscale Service Options (Custom Mode) ${CClear}" echo -e "${CWhite}PRECMD: ${CGreen}$precmd" echo -e "${CWhite}ARGS: ${CGreen}$args" echo -e "${CWhite}PREARGS: ${CGreen}$preargs" fi echo "" echo -e "${InvDkGray}${CWhite}Tailscale Connection Commandline ${CClear}" if [ "$tsoperatingmode" == "Custom" ]; then echo -e "${CWhite}${CGreen}$customcmdline${CClear}" else if [ $exitnode -eq 1 ]; then exitnodecmd="--advertise-exit-node "; else exitnodecmd=""; fi if [ $advroutes -eq 1 ]; then advroutescmd="--advertise-routes=$routes "; else advroutescmd=""; fi if [ $accroutes -eq 1 ]; then accroutescmd="--accept-routes"; else accroutescmd=""; fi echo -e "${CWhite}${CGreen}$exitnodecmd$advroutescmd$accroutescmd${CClear}" fi echo "" #read -rsp $'Press any key to continue...\n' -n1 key else echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Tailscale binaries not found. Please investigate." >> $logfile tsinstalled=0 exec sh /jffs/scripts/tailmon.sh -setup fi #Determine if a TAILMON autoupdate has happened and restart script if [ -f /jffs/addons/tailmon.d/updated.txt ] then printf "\33[2K\r" printf "${CGreen}\r[Replacing TAILMON with Latest Version]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - INFO: Replacing TAILMON with latest version." >> $logfile sleep 1 rm -f /jffs/addons/tailmon.d/updated.txt >/dev/null 2>&1 exec sh /jffs/scripts/tailmon.sh exit 0 fi #Determine if S06tailscaled service settings have changed if [ $tsinstalled -eq 1 ] && [ $persistentsettings -eq 1 ]; then s06args=$(cat /opt/etc/init.d/S06tailscaled | grep ^ARGS= | cut -d '=' -f 2-) 2>/dev/null tailmonargs="\"$args\"" if [ "$s06args" != "$tailmonargs" ]; then printf "\33[2K\r" printf "${CGreen}\r[Tailscale Service settings out-of-sync]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Tailscale Service settings are out-of-sync." >> $logfile sleep 1 tsdown stopts #make mods to the S06tailscaled service for Userspace mode if [ "$tsoperatingmode" == "Userspace" ]; then applyuserspacemode #make mods to the S06tailscaled service for Kernel mode elif [ "$tsoperatingmode" == "Kernel" ]; then applykernelmode #make mods to the S06tailscaled service for Custom mode elif [ "$tsoperatingmode" == "Custom" ]; then applycustomchanges fi printf "\33[2K\r" printf "${CGreen}\r[Tailscale Service settings synced]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Tailscale Service settings synced." >> $logfile sleep 1 startts tsup echo "" sendmessage 1 "Tailscale Service settings out-of-sync" resettimer=1 fi fi #Determine if Tailscale service is down if [ $tsinstalled -eq 1 ] && [ $keepalive -eq 1 ]; then if [ $tsservice -ne 0 ]; then printf "\33[2K\r" printf "${CGreen}\r[Tailscale Service appears dead]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Tailscale Service appears dead." >> $logfile sleep 1 tsdown stopts startts tsup resettimer=1 echo "" sendmessage 1 "Tailscale Service Restarted" exec sh /jffs/scripts/tailmon.sh -noswitch fi fi #Determine if Tailscale status is producing an error if [ $tsstatus -ne 0 ]; then printf "\33[2K\r" printf "${CGreen}\r[Tailscale Status producing errors...Restarting services]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - ERROR: Tailscale Status producing errors. Restarting services." >> $logfile sleep 1 tsdown stopts startts tsup resettimer=1 echo "" sendmessage 1 "Tailscale Service Restarted" fi #Determine if router rebooted #uptime=$(awk '{printf("%03dd %02dh %02dm %02ds\n",($1/60/60/24),($1/60/60%24),($1/60%60),($1%60))}' /proc/uptime) uptimedays=$(awk '{printf("%1d\n",($1/60/60/24))}' /proc/uptime) uptimehrs=$(awk '{printf("%1d\n",($1/60/60%24))}' /proc/uptime) uptimemins=$(awk '{printf("%1d\n",($1/60%60))}' /proc/uptime) if [ $uptimedays -eq 0 ] && [ $uptimehrs -eq 0 ] && [ $uptimemins -le 10 ] && [ $routerboot -eq 0 ]; then # Router must have rebooted and send a notification printf "\33[2K\r" printf "${CGreen}\r[Router appears to have been restarted]" echo -e "$(date +'%b %d %Y %X') $($timeoutcmd$timeoutsec nvram get lan_hostname) TAILMON[$$] - WARNING: Router appears to have been unexpectedly restarted." >> $logfile sleep 1 echo "" sendmessage 1 "Router has been restarted" routerboot=1 fi #display a standard timer if [ "$resettimer" == "0" ]; then timer=0 while [ $timer -ne $timerloop ] do timer=$(($timer+1)) preparebar 46 "|" progressbaroverride $timer $timerloop "" "s" "Standard" if [ "$resettimer" == "1" ]; then timer=$timerloop; fi done fi resettimer=0 done exit 0