Advertisement
MartineauPASTEBIN

IoTBlock.sh

Oct 26th, 2018
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 27.35 KB | None | 0 0
  1. #!/bin/sh
  2. VER="v1.03b1"
  3. #======================================================================================== © 2018 Martineau, v1.03b1
  4. #
  5. # Track/Block unsolicited outbound traffic from the IoT devices such as Alexa-capable IPs
  6. #
  7. #     IoTBlock     [-h|help] { 'iot_group' {[ init  | del | status} ] } [ track ] [ noapps] [ report ] [ onerule ] [ forcewan ]
  8. #                  [all status] [blockntp] [localntp='ntp_server']
  9. #
  10. #     IoTBlock     alexa init
  11. #                  IPSET 'Alexa' will be populated with devices from group 'ALEXA' in '/jffs/configs/IPGroups', and Chain 'MyAlexa' will be created
  12. #                  User defined apps firewall rules are added if defined in '/jffs/configs/IoT_Alexa.apps'
  13. #     IoTBlock     alexa del
  14. #                  IPSET 'Alexa' will be flushed, and Chain 'MyAlexa' will be deleted
  15. #     IoTBlock     alexa status
  16. #                  IPSET 'Alexa' will be listed together with Chain statistics
  17. #     IoTBlock     alexa init block
  18. #                  Block ALL i.e. no known ACCEPT rules will be used
  19. #     IoTBlock     alexa report
  20. #                  Scan Syslog for 'BADAlexa' messages and report basic Port/DST/SRC/Protocol statistics.
  21. #                  Then these ports can be added to '/jffs/configs/IoT_Alexa.apps'
  22. #     IoTBlock     all status
  23. #                  All IPSETs will be listed together with Chain statistics
  24. #     IoTBlock     alexa init noapps
  25. #                  No apps rules are added from '/jffs/configs/IoT_Alexa.apps'
  26. #     IoTBlock     alexa init onerule
  27. #                  User defined apps firewall rules are added if defined in '/jffs/configs/IoT_Alexa.apps' only once in the chain; not per device
  28. #     IoTBlock     alexa init forcewan
  29. #                  Replace '-o xxx' -> '-o $WAN_IF' or insert it if not specified.
  30. #     IoTBlock     alexa init localntp=192.168.0.1
  31. #                  As per example one, but it is assumed that NTPD service is running on the router so Alexa group will use it.
  32. #
  33. # /jffs/configs/IPGroups
  34. #      ALEXA     Echo-Show,Echo-Dot-KT,Echo-Dot-Guest
  35. #
  36. # /jffs/scripts/firewall-start
  37. #      /jffs/scripts/IoTBlock.sh alexa init
  38. #      /jffs/scripts/IoTBlock.sh lifx init track noapps
  39. #      /jffs/scripts/IoTBlock.sh google init track forcewan
  40.  
  41. # [URL="https://www.snbforums.com/threads/help-please-need-assistance-stopping-outbound-connections.38086/page-5#post-377324"]IoT VLAN Netdata graph[/URL]
  42. #*************************************FUNCTIONS***************************************************************
  43. # Print between line beginning with'#==' to first blank line inclusive
  44. ShowHelp() {
  45.     awk '/^#==/{f=1} f{print; if (!NF) exit}' $0
  46. }
  47. Say(){
  48.    echo -e $$ $@ | logger -st "($(basename $0))"
  49. }
  50. SayT(){
  51.    echo -e $$ $@ | logger -t "($(basename $0))"
  52. }
  53. # shellcheck disable=SC2034
  54. ANSIColours() {
  55.  
  56.     cRESET="\e[0m";cBLA="\e[30m";cRED="\e[31m";cGRE="\e[32m";cYEL="\e[33m";cBLU="\e[34m";cMAG="\e[35m";cCYA="\e[36m";cGRA="\e[37m"
  57.     cBGRA="\e[90m";cBRED="\e[91m";cBGRE="\e[92m";cBYEL="\e[93m";cBBLU="\e[94m";cBMAG="\e[95m";cBCYA="\e[96m";cBWHT="\e[97m"
  58.     aBOLD="\e[1m";aDIM="\e[2m";aUNDER="\e[4m";aBLINK="\e[5m";aREVERSE="\e[7m"
  59.     cRED_="\e[41m";cGRE_="\e[42m"
  60.  
  61. }
  62. # Function Parse(String delimiter(s) variable_names)
  63. Parse() {
  64.     #
  65.     #   Parse       "Word1,Word2|Word3" ",|" VAR1 VAR2 REST
  66.     #               (Effectivley executes VAR1="Word1";VAR2="Word2";REST="Word3")
  67.  
  68.     local string IFS
  69.  
  70.     TEXT="$1"
  71.     IFS="$2"
  72.     shift 2
  73.     read -r -- "$@" <<EOF
  74. $TEXT
  75. EOF
  76. }
  77. Check_Router_Mode() {
  78.     local OK=1                              # Assume not Router mode
  79.     case "$(nvram get sw_mode)" in
  80.         0) SW_MODE="Unconfigured";;
  81.         1) SW_MODE="Router";OK=0;;
  82.         2) SW_MODE="Repeater";;
  83.         3) SW_MODE="AP";;
  84.         4) SW_MODE="Hotspot";;
  85.         *) SW_MODE="Unknown nvram sw_mode value="$(nvram get sw_mode);;
  86.     esac
  87.     echo $SW_MODE
  88.     return $OK
  89. }
  90. Get_WAN_IF_Name () {
  91.  
  92.     local IF_NAME=$(nvram get wan0_ifname)              # DHCP/Static ?
  93.  
  94.     # Usually this is probably valid for both eth0/ppp0e ?
  95.     if [ "$(nvram get wan0_gw_ifname)" != "$IF_NAME" ];then
  96.         local IF_NAME=$(nvram get wan0_gw_ifname)
  97.     fi
  98.    
  99.     if [ ! -z "$(nvram get wan0_pppoe_ifname)" ];then
  100.         local IF_NAME="$(nvram get wan0_pppoe_ifname)"      # PPPoE
  101.     fi
  102.  
  103.     echo $IF_NAME
  104.  
  105. }
  106. Firewall(){
  107.     DUMMY="################################################################################## 102"
  108.     local JUNK=$(echo -e $@ | sed 's/"/""/g')
  109.    
  110.     iptables $@ 2>/dev/null         # Suppress error messages
  111.     local FIREWALL_ERROR=$?
  112.  
  113.     if [ "$FIREWALL_ERROR" -gt 0 ];then             # Report error for "-I" insert and '-A' append actions
  114.         if [ "$1" == "-I" ] || [ "$1" == "-A" ];then
  115.             # Hack using I/O to file!
  116.             iptables $@ 2>/tmp/iptables.error$$; echo -e $cBRED"\t" $(grep "" -m1 /tmp/iptables.error$$);rm /tmp/iptables.error$$       # Hack using I/O to file!
  117.             echo -e "\t\t==> $@"
  118.             return 1
  119.         fi
  120.     fi
  121.  
  122.     return 0
  123. }
  124. ExpandIPRange() {
  125.  
  126.     # '192.168.1.30 192.168.1.50-192.168.1.54' -> '192.168.1.30 192.168.1.50 192.168.1.51 192.168.1.52 192.168.1.53 192.168.1.54'
  127.  
  128.     local HOST_NAME=0                                   # Hostname found/returned
  129.     local IP_LIST=
  130.     local START_RANGE=
  131.     local END_RANGE=
  132.     local NUM=
  133.     local MAX=
  134.  
  135.     local LANIPADDR=`nvram get lan_ipaddr`
  136.     local LAN_PREFIX=${LANIPADDR%.*}                    # 1.2.3.99 -> 1.2.3
  137.  
  138.     for THIS in $@
  139.         do
  140.        
  141.             if [ -n "$(echo "$THIS" | grep -E "^#")" ];then
  142.                 break               # Ignore comment
  143.             fi
  144.            
  145.             # If any alphabetic characters then assume it is a name e.g. LIFX-Table_light
  146.             if [ -z "$(echo $THIS | grep "[A-Za-z]")" ];then
  147.  
  148.                 if [ -n "$(echo $THIS | grep "-")" ];then
  149.  
  150.                     Parse $THIS "-" START_RANGE END_RANGE               # 1.2.3.90-1.2.3.99 -> 1.2.3.90 1.2.3.99
  151.                     local START_PREFIX=${START_RANGE%.*}                # 1.2.3.90 -> 1.2.3
  152.                     local END_PREFIX=${END_RANGE%.*}                    # 1.2.3.99 -> 1.2.3
  153.  
  154.                     if [ "$START_PREFIX" != "$END_PREFIX" ];then        # Restrict range of devices to 254
  155.                         Say "***ERROR*** invalid IP range" $THIS
  156.                         echo ""
  157.                         return 100
  158.                     fi
  159.  
  160.                     NUM=${START_RANGE##*.}                              # Extract 4th octet 1.2.3.90 -> 90
  161.                     MAX=${END_RANGE##*.}                                # Extract 4th octet 1.2.3.99 -> 99
  162.                     while [ $NUM -le $MAX ]
  163.                         do
  164.                             IP_LIST=$IP_LIST" "$START_PREFIX"."$NUM
  165.                             NUM=$(($NUM+1))
  166.                         done
  167.                 else
  168.                     local THIS_PREFIX=${THIS%.*}
  169.                     if [ "$THIS_PREFIX" != "$LAN_PREFIX" ];then
  170.                         Say "***ERROR '"$THIS"' is not on this LAN '"$LAN_PREFIX".0/24'"
  171.                         echo ""
  172.                         return 200
  173.                     else
  174.                         IP_LIST=$IP_LIST" "$THIS                        # Add to list
  175.                     fi
  176.                 fi
  177.             else
  178.                 # Let the caller ultimately decide if non-IP is valid!!!
  179.                 #Say  "**Warning non-IP" $THIS
  180.                 IP_LIST=$IP_LIST" "$THIS                                # Add to list
  181.                 HOST_NAME=1
  182.             fi
  183.  
  184.             shift 1
  185.         done
  186.  
  187.     echo $IP_LIST
  188.  
  189.     if [ $HOST_NAME -eq 1 ];then
  190.         return 300
  191.     else
  192.         return 0
  193.     fi
  194. }
  195. Convert_TO_IP () {
  196.  
  197.     # Perform a lookup if a hostname (or I/P address) is supplied and is not known to PING
  198.     # NOTE: etc/host.dnsmasq is in format
  199.     #
  200.     #       I/P address    hostname
  201.     #
  202.  
  203.     local USEPATH="/jffs/configs"
  204.  
  205.     if [ -n "$1" ];then
  206.  
  207.         if [ -z $2 ];then                                   # Name to IP Address
  208.            local IP_NAME=$(echo $1 | tr '[a-z]' '[A-Z]')
  209.  
  210.            local IP_RANGE=$(ping -c1 -t1 -w1 $IP_NAME 2>&1 | tr -d '():' | awk '/^PING/{print $3}')
  211.  
  212.            # 127.0.53.53 for ANDROID? https://github.com/laravel/valet/issues/115
  213.            if [ -n "$(echo $IP_RANGE | grep -E "^127")" ];then
  214.               local IP_RANGE=
  215.            fi
  216.  
  217.            if [ -z "$IP_RANGE" ];then       # Not PINGable so lookup static
  218.  
  219.               IP_RANGE=$(grep -i "$IP_NAME" /etc/hosts.dnsmasq  | awk '{print $1}')
  220.               #Say "Lookup '$IP_NAME' in DNSMASQ returned:>$IP_RANGE<"
  221.  
  222.               # If entry not matched in /etc /hosts.dnsmasq see if it exists in our IPGroups lookup file
  223.               #
  224.               #       KEY     I/P address[ {,|-} I/P address]
  225.               #
  226.               if [ -z "$IP_RANGE" ] && [ -f $USEPATH/IPGroups ];then
  227.                  #IP_RANGE=$(grep -i "^$IP_NAME" $USEPATH/IPGroups | awk '{print $2}')
  228.                  IP_RANGE=$(grep -i "^$IP_NAME" $USEPATH/IPGroups | awk '{$1=""; print $0}' | tr ',' ' ' | tr ':' '-')
  229.                  Say "Lookup '$IP_NAME' in '$USEPATH/IPGroups' returned:>$IP_RANGE<"
  230.               fi
  231.            fi
  232.         else                                                # IP Address to name
  233.             IP_RANGE=$(nslookup $1 | grep "Address" | grep -v localhost | cut -d" " -f4)
  234.         fi
  235.     else
  236.        local IP_RANGE=                                  # Return a default WiFi Client????
  237.        #IP_NAME="Nexus-7"
  238.        #IP_RANGE=`grep -i $IP_NAME /etc/hosts.dnsmasq  | awk '{print $1}'`
  239.        #Say "DEFAULT '$IP_NAME' lookup returned:>$IP_RANGE<"
  240.     fi
  241.  
  242.     echo $IP_RANGE
  243. }
  244. Hostname_from_IP () {
  245.  
  246.     local HOSTNAMES=
  247.  
  248.     for IP in $@
  249.         do
  250.             local HOSTNAME=$(Convert_TO_IP "$IP" "Reverse")
  251.             HOSTNAMES=$HOSTNAMES" "$HOSTNAME
  252.         done
  253.     echo $HOSTNAMES
  254. }
  255. Chain_exists() {
  256.  
  257.     # Args: {chain_name} [table_name]
  258.  
  259.     local CHAIN="$1"
  260.     shift
  261.  
  262.     [ $# -eq 1 ] && local TABLE="-t $1"
  263.  
  264.     iptables $TABLE -n -L $CHAIN >/dev/null 2>&1
  265.     local RC=$?
  266.     if [ $RC -ne 0 ];then
  267.         echo "N"
  268.         return 1
  269.     else
  270.         echo "Y"
  271.         return 0
  272.     fi
  273. }
  274. Print_IPSET () {
  275.  
  276.         echo -en "\n"
  277.  
  278.         #ipset $LIST $1 | head | grep -v "^[0-9]"           # Sadly 'ipset -t xxxxxx' to list only the IPSET header doesn't work on Asus
  279.         ipset $LIST $1 -t                                   # IPSET v6 does!!!!
  280.         if [ "$2" == "list" ] && [ -z "$3" ] ;then          # Verbose if $2=list'
  281.             ipset $LIST $1                                  | \
  282.                 grep -E "^[0-9]"                            | \
  283.                 sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4    | \
  284.                 awk ' {printf "%15s\t", $1;}'
  285.             echo -e "\n"
  286.             echo -e "\tTotal="$(ipset $LIST $1 | grep -E "^[0-9]" | wc -l)"\n"
  287.         #else
  288.             # ipset $LIST $1                                    | \
  289.                 # grep -E "^[0-9]" | grep -E "timeout 0$"       | \
  290.                 # sort -n -t . -k 1,1 -k 2,2 -k 3,3 -k 4,4  | \
  291.                 # awk ' {printf "%15s\t", $1;}'
  292.             # echo -e "\n"
  293.             # echo -e "\tTotal="$(ipset $LIST $1 | grep -E "^[0-9]" | grep -E "timeout 0$" | wc -l)"\n"
  294.         fi
  295.  
  296. }
  297. Show_Status () {
  298.  
  299.     local IOT_GROUPS=$1
  300.  
  301.     if [ "$1" == "All" ];then
  302.         local IOT_GROUPS=$(iptables -nvL --line -t filter | awk '/match-set/ {print $(NF-1)}')
  303.     fi
  304.  
  305.     for IOT_GROUP in $IOT_GROUPS
  306.         do
  307.             if [ "$2" == "IPSET" ];then
  308.  
  309.                 # Print devices
  310.                 echo -e $cBMAG >&2
  311.  
  312.                 ipset $LIST $IOT_GROUP >/dev/null 2>&1;if [ $? -eq 0 ]; then
  313.                     if [ -z "$(echo "$1" | grep -oiE "$IOT_PRODUCTS")" ];then
  314.                         Print_IPSET $IOT_GROUP "list"
  315.                     else
  316.                         ipset $LIST $IOT_GROUP
  317.                     fi
  318.                 else
  319.                     echo -e $cBRED"\a" >&2
  320.                     Say "***ERROR '"$IOT_GROUP"' IPSET does not exist!"
  321.                 fi
  322.                 echo -e
  323.                 Print_IPSET  ${IOT_GROUP}TRK "list"
  324.             fi
  325.  
  326.             # Display rules
  327.             echo -e $cBYEL"\n\tIoT rules:\n" >&2
  328.  
  329.             if [ "$(Chain_exists "My"$IOT_GROUP)" == "Y" ];then
  330.                 #$IPT -nvL $CHAIN --line -t filter | grep -E "$CHAIN|pkts bytes|!tun2+|udp dpt:123"$REGEXP      # Display I/P Addresses
  331.                 $IPT -nvL "My"$IOT_GROUP --line -t filter
  332.             fi
  333.            
  334.             # Were any "localntp="
  335.             if [ -n "$($IPT -t nat -nvL PREROUTING | grep -E ":123.*$IOT_GROUP")" ];then
  336.                 echo -e $cBCYA"\n\tNTP redirect rules:\n" >&2
  337.                 $IPT -t nat --line -nvL PREROUTING | grep -E "Chain|pkts|:123.*$IOT_GROUP"
  338.             fi
  339.  
  340.             if [ $SCANLOG -eq 1 ];then
  341.                 echo -e $cBCYA >&2
  342.                 Scan_Syslog
  343.             fi
  344.         done
  345.  
  346. }
  347. Scan_Syslog () {
  348.  
  349.     if [ -z "$(ps | grep -v grep | grep -m 1 syslog-ng)" ];then             # v1.02
  350.         local SYSLOG="/tmp/syslog.log"
  351.     else
  352.         local SYSLOG="/opt/var/log/messages"
  353.     fi
  354.    
  355. # Useful scan log function for port traffic
  356. #grep "XXXXX" $SYSLOG   | grep -vE "443|80|123|53|ICMP" | sed 's/^.*SRC=//;s/LEN=.*PROTO=//;s/.SEQ=.*$//;s/DST=//;s/LEN=.*$//' | sort -r | uniq -c | awk ' {printf "%5d %-16s-> %-15s %3s  %-9s  %-9s  http://www.speedguide.net/port.php?port=%s\n", $1,$2,$3,$4,$5,$6, substr($6,5)}'
  357.    
  358.     grep "BAD"$IOT_GROUP $SYSLOG    |   sed 's/^.*SRC=//;s/LEN=.*PROTO=//;s/.SEQ=.*$//;s/DST=//;s/LEN=.*$//' | \
  359.                                         sort -r | uniq -c | \
  360.                                         awk ' {printf "%5d %-16s-> %-15s %3s  %-9s  %-9s  http://www.speedguide.net/port.php?port=%s\n", $1,$2,$3,$4,$5,$6, substr($6,5)}'
  361.  
  362.     echo -e $cBYEL"\n\nUnknown TCP/UDP Ports" >&2
  363.     # Ignore known TCP and UDP ports
  364.     ipset list ${IOT_GROUP}TRK | grep -vE "tcp:[443|80|53]" | grep -vE "udp:[53]"
  365.  
  366.     grep "BAD"$IOT_GROUP $SYSLOG    |   grep -vE "DPT=[443|80|53]" | sed 's/^.*SRC=//;s/LEN=.*PROTO=//;s/.SEQ=.*$//;s/DST=//;s/LEN=.*$//' | \
  367.                                         sort -r | uniq -c | \
  368.                                         awk ' {printf "%5d %-16s-> %-15s %3s  %-9s  %-9s  http://www.speedguide.net/port.php?port=%s\n", $1,$2,$3,$4,$5,$6, substr($6,5)}'
  369. }
  370. IPv4_nslookup () {
  371.  
  372.     local IP_LIST=$(nslookup $1| grep -woE '([0-9]{1,3}\.){3}[0-9]{1,3}' | grep -v 127.0.0.1)
  373.  
  374.     echo "$IP_LIST"
  375.  
  376.     if [ -n "$IP_LIST" ];then
  377.         return 0
  378.     else
  379.         return 1
  380.     fi
  381.  
  382. }
  383. #======================================Main==================================================================================
  384. Main() { true; }            # Syntax that is Atom Shellchecker compatible!
  385.  
  386. ANSIColours
  387.  
  388.  
  389. # v384.13+ NVRAM variable 'lan_hostname' supersedes 'computer_name'
  390. [ -n "$(nvram get computer_name)" ] && MYROUTER=$(nvram get computer_name) || MYROUTER=$(nvram get lan_hostname)
  391.  
  392. FIRMWARE=$(echo $(nvram get buildno) | awk 'BEGIN { FS = "." } {printf("%03d%02d",$1,$2)}')
  393.  
  394. WAN_IF=$(Get_WAN_IF_Name)
  395.  
  396. # Can only run in Router Mode;
  397. if [ "$(Check_Router_Mode)" != "Router" ];then
  398.     echo -e $cBRED"\a\n\n\n\n\t\t\t\t** "$(Check_Router_Mode)" mode is not supported **\t\t\t\t\t\n\n\n"$cRESET
  399.     exit 999
  400. fi
  401.  
  402. # Need assistance!???
  403. if [ "$1" == "help" ] || [ "$1" == "-h" ]; then
  404.     echo -e $cBWHT
  405.     ShowHelp
  406.     echo -e $cRESET
  407.     exit 0
  408. fi
  409.  
  410. if [ $(echo $@ | grep -cw "debug") -gt 0 ];then         # 'debug'   requested?
  411.     DEBUG="debug"
  412.     if [ "$1" == "debug" ];then
  413.         shift                                               # Remove from arg list!
  414.     fi
  415.     echo -e "\n\n\t\t"$cBWHT$aBLINK"DEBUG mode enabled"\t"$cBWHT$aBLINK"DEBUG mode enabled"\n\n"$cRESET
  416.     set -x                                                  # Enable trace
  417. fi
  418.  
  419. echo -e $cBWHT
  420.  
  421. Say $VER "IoT Firewall blocking...." $@
  422.  
  423. IPT="/usr/sbin/iptables"
  424.  
  425. modprobe -sv xt_comment.ko                                      # v1.10
  426. [ $? -gt 0 ] && { echo -e $cBRED"\a\n\t***ERROR modprobe unable to load iptables 'comment' module 'xt_comment.ko'\n"$cRESET; exit 90; }
  427.  
  428. # 380.63+ for ARM routers, IPSET v6  is available...Load appropriate IPSET modules
  429. case $(ipset -v | grep -o "v[4,6]") in
  430.   v6) MODULES="ARM"; MATCHSET='--match-set'; LIST='list'; CREATE='create'; SAVE='save'; RESTORE='restore'; FLUSH='flush'; DESTROY='destroy'; ADD='add'; SWAP='swap'; TEST='test'; DELETE="del"
  431.       IPHASH='hash:ip'; NETHASH='hash:net'; PORTBITMAP="bitmap:port range 1-65535"; IPPORT=; SETNOTFOUND='name does not exist'; TIMEOUT='timeout'
  432.       lsmod | grep -q "xt_set" || for module in ip_set ip_set_hash_net ip_set_hash_ip xt_set
  433.       do modprobe $module; done;;
  434.   v4) MODULES="MIPS";MATCHSET='--set'; LIST='--list'; CREATE='--create'; SAVE='--save'; RESTORE='--restore'; FLUSH='--flush'; DESTROY='--destroy'; ADD='--add'; SWAP='--swap'; TEST='--test'; DELETE="--del"
  435.       IPHASH='iphash'; NETHASH='nethash'; PORTBITMAP="portmap --from 1 --to 65535"; IPPORT="ipporthash"; SETNOTFOUND='Unknown set'; TIMEOUT=; RETAIN_SECS=
  436.       lsmod | grep -q "ipt_set" || for module in ip_set ip_set_nethash ip_set_iphash ipt_set
  437.       do modprobe $module; done;;
  438.   *) Say "**ERROR** Unknown ipset version: $(ipset -v). Exiting." && (echo -e "\a";exit 99);;
  439. esac
  440.  
  441. # USB Location of IPSET save/restore configuration directory specified?
  442. if [ "$(echo $@ | grep -o "dir=" | wc -w )" -eq 1 ];then
  443.     #Parse "$(echo $@ | grep -oE "dir=.*")" "=" v1 DIR
  444.     #DIR=$(echo $DIR | sed 's/ //g')                        # Tacky! regexp should be used properly!
  445.     DIR=$(echo $@ | sed -n "s/^.*dir=//p" | awk '{print $1}')
  446.     if [ ! -d $DIR ];then   # Must be only digits and not blank or '0'
  447.     SayT $VER "***ERROR 'dir=$DIR' directory invalid! - ABORTing"
  448.         echo -e $cBRED"\a\n\t***ERROR $aBLINK'dir=$DIR'\e[25m directory invalid! - ABORTing\n"$cRESET
  449.         exit 94
  450.     fi
  451. else
  452.     if [ -d  "/tmp/mnt/"$MYROUTER ];then
  453.         DIR="/tmp/mnt/"$MYROUTER                # <== USB Location of IPSET save/restore configuration
  454.     else
  455.         DIR="/tmp"                              #         NOTE: /TMP isn't permanent! ;-) but allows testing of save/restore
  456.     fi
  457. fi
  458.  
  459. echo -en $cBRED
  460.  
  461.  
  462. IOT_PRODUCTS="Alexa|Hive|Tplink|Lifx|Nest|Hue|Ring|WeMo|Google|Roku"
  463.  
  464. IOT_GROUP="all"
  465. if [ -n "$1" ] && [ "$1" != "status" ] && [ "$1" != "report" ] && [ "$1" != "init" ];then
  466.     IOT_GROUP=$1
  467. fi
  468.  
  469. # Change 'alexa' -> Alexa and 'iot' -> 'Iot'
  470. IOT_GROUP=$(echo $IOT_GROUP | awk '{print toupper(substr($0,0,1))tolower(substr($0,2))}')
  471.  
  472. # Don't clutter the FORWARD chain
  473. CHAIN="My"$IOT_GROUP
  474.  
  475. APP_RULES=                          # Add Known Application port rules
  476. if [ "$( echo $@ | grep -cw "noapps" )" -eq 1 ];then
  477.     APP_RULES="noapps"
  478. fi
  479.  
  480. TRACK_PORTS=1                       # Use 'logaccept' rather than ACCEPT
  481.  
  482. ALLOW_ALL=0
  483. if [ "$( echo $@ | grep -cw "track" )" -eq 1 ];then
  484.     ALLOW_ALL=1
  485. fi
  486.  
  487. SYSLOGMSG=                          # Write Syslog [BADxxxx] messages
  488. if [ "$( echo $@ | grep -cw "logmsg" )" -eq 1 ];then
  489.     SYSLOGMSG="logmsg"
  490. fi
  491.  
  492. SCANLOG=0
  493. if [ "$( echo $@ | grep -cw "report" )" -eq 1 ];then
  494.     SCANLOG=1
  495. fi
  496.  
  497. ONE_RULE=                           # Create apps rules for each device
  498. if [ "$( echo $@ | grep -cw "onerule" )" -eq 1 ];then
  499.     ONE_RULE="onerule"                      # Create one apps rule for the group chain
  500. fi
  501.  
  502. FORCEWAN=                           # Ensure '-o $WAN_IF' is in apps firewall rule
  503. if [ "$( echo $@ | grep -cw "forcewan" )" -eq 1 ];then
  504.     FORCEWAN="forcewan"
  505. fi
  506.  
  507. TRACK_NTP=0
  508. if [ "$( echo $@ | grep -cw "trackntp" )" -eq 1 ];then
  509.     TRACK_NTP=1
  510. fi
  511.  
  512. BLOCKNTP=0
  513. if [ "$( echo $@ | grep -cw "blockntp=" )" -eq 1 ];then
  514.     BLOCKNTP=1
  515. fi
  516.  
  517. LOCALNTP=
  518. if [ "$( echo $@ | grep -c "localntp=" )" -eq 1 ];then
  519.     LOCALNTP=$(echo $@ | sed -n "s/^.*localntp=//p" | awk '{print $1}')
  520. fi
  521.  
  522. if [ "$2" == "status" ] || [ "$2" == "report" ] || [ "$1" == "status" ] || [ "$1" == "report" ];then
  523.         echo -en $cBCYA
  524.         Show_Status $IOT_GROUP "IPSET"
  525.         echo -e $cRESET
  526.         exit 0
  527. fi
  528.  
  529. # Check for group names, and expand as necessary
  530. #   e.g. '192.168.1.30,192.168.1.50-192.168.1.54' -> '192.168.1.30 192.168.1.50 192.168.1.51 192.168.1.52 192.168.1.53 192.168.1.54'
  531. if [ -f "/jffs/configs/IPGroups" ];then     # '/jffs/configs/IPGroups' two columns
  532.                                             # ID xxx.xxx.xxx.xxx[[,xxx.xxx.xxx.xxx][-xxx.xxx.xxx.xxx]
  533.     #IOT_DEVICES=$(grep -iwE "^$IOT_GROUP" /jffs/configs/IPGroups | awk '{print $2}')
  534.     IOT_DEVICES=$(grep -iwE "^$IOT_GROUP" /jffs/configs/IPGroups | awk '{$1=""; print $0}')
  535. else
  536.     #IOT_DEVICES="10.88.8.120,10.88.8.122-10.88.8.123,10.88.8.124:10.88.8.125"      # Silly example to illustrate how multiple ranges can be specified!
  537.     IOT_DEVICES=
  538. fi
  539.  
  540. # Expand the list of IoT IPs as necessary
  541. #   e.g. '192.168.1.30,192.168.1.50-192.168.1.54' -> '192.168.1.30 192.168.1.50 192.168.1.51 192.168.1.52 192.168.1.53 192.168.1.54'
  542. IOT_DEVICES=$(echo $IOT_DEVICES | tr ',' ' ')                               # CSVs ?
  543. IOT_DEVICES=$(echo $IOT_DEVICES | tr ':' '-')                               # Alternative range spec xxx.xxx.xxx.xxx:xxx.xxx.xxx.xxx
  544.  
  545. # Expand any ranges - allow Hostnames e.g. LIFX-Table_light to pass through
  546. if [ -n  "$(echo "$IOT_DEVICES" | grep "-")" ];then                 # xxx-yyy range ?
  547.     IOT_DEVICES="$(ExpandIPRange "$IOT_DEVICES")"
  548.     RC=$?                                                                   # Should really check
  549. fi
  550.  
  551. if [ -z "$IOT_DEVICES" ];then
  552.     echo -e $cBRED
  553.     Say "***ERROR No IoT '"$IOT_GROUP"' group devices defined - see /jffs/configs/IPGroups RC="$RC
  554.     echo -e "\a"$cRESET
  555.     exit 1
  556. fi
  557.  
  558. # Create or Delete rules for the specified IoT group of devices
  559. if [ "$2" == "init" ] || [ "$2" == "del" ];then
  560.  
  561.     case $2 in
  562.         init)
  563.             # If IoT IPSET doesn't exist then create it, else wipe its contents
  564.             ipset $LIST $IOT_GROUP >/dev/null 2>&1;if [ $? -ne 0 ]; then
  565.                 ipset $CREATE $IOT_GROUP $IPHASH comment
  566.             else
  567.                 ipset $FLUSH $IOT_GROUP
  568.             fi
  569.             # If IoT Tracking IPSET doesn't exist then create it
  570.             ipset $LIST ${IOT_GROUP}TRK >/dev/null 2>&1;if [ $? -ne 0 ]; then
  571.                 ipset $CREATE ${IOT_GROUP}TRK $IPHASH,port comment
  572.             fi
  573.  
  574.             #for DEVICE in Echo_Show Echo_Bedroom Echo_Guest HS110-Lounge1 HS110-Lounge2 \
  575.             #              LIFX-Table LIFX-Reading Hive-Hub Nest1 Ring-Doorbell Chromecast-Main
  576.             for DEVICE in $IOT_DEVICES
  577.                 do
  578.                
  579.                     if [ -n "$(echo "$DEVICE" | grep -E "^#")" ];then
  580.                         break                                   # Comment denotes end of devices
  581.                     fi
  582.                    
  583.                     ADD_COMMENT=
  584.                     REVERSE_IP=0
  585.                     # Cosmetic...add description for device if not already in IP format
  586.                     if [ -n "$(echo "$IOT_GROUP" | grep -oiE "$IOT_PRODUCTS")" ] && [ -z "$(echo $DEVICE | grep -E '^([0-9]{1,3}\.){3}[0-9]{1,3}$')" ];then
  587.                         ADD_COMMENT=$DEVICE
  588.                     fi
  589.  
  590.                     # Since we are dealing with possibly IoT groups within IoT groups...
  591.                     IP_LIST=$(Convert_TO_IP "$DEVICE")
  592.  
  593.                     if [ "$(echo $IP_LIST | wc -w)" -gt 1 ];then
  594.                         ADD_COMMENT="IoT Group: ${DEVICE}"
  595.                         REVERSE_IP=1                                            # Reverse lookup this group IP?
  596.                     else
  597.                         ADD_COMMENT=$DEVICE
  598.                     fi
  599.                     for IP in $IP_LIST
  600.                         do
  601.                             HOSTNAMETXT=
  602.                             if [ $REVERSE_IP -eq 1 ];then
  603.                                 HOSTNAMETXT="-->("$(Convert_TO_IP $IP "reverse")")"
  604.                             fi
  605.                             ipset add $IOT_GROUP $IP comment "${ADD_COMMENT}$HOSTNAMETXT"
  606.                         done
  607.                 done
  608.             ;;
  609.         del)
  610.             # Trash the IPSET?
  611.             # Hmmm can only flush contents at this point....
  612.             NOW=$(date +"%Y%m%d-%H%M%S")    # current date and time
  613.             if [ -f $DIR/$IOT_GROUP.config ];then
  614.                 cp $DIR/$IOT_GROUP.config $DIR/$IOT_GROUP.config-$NOW           # Create restore backup
  615.                 rm $DIR/$IOT_GROUP.config
  616.             fi
  617.             ipset $LIST $IOT_GROUP >/dev/null 2>&1;if [ $? -eq 0 ]; then
  618.                 ipset $FLUSH $IOT_GROUP
  619.                 fi
  620.             ;;
  621.     esac
  622.  
  623.     # Firewall modification
  624.     if [ "$2" == "init" ];then          # Create rules (but delete them if they already exist to prevent duplicates)
  625.         ACTIONS="-D -I"
  626.     else
  627.         ACTIONS="-D"                    # Delete rules
  628.     fi
  629.  
  630.     FWRULENO=
  631.  
  632.     for ACTION in $ACTIONS
  633.         do
  634.             if [ "$ACTION" == "-I" ];then
  635.                 if [ "$(Chain_exists "$CHAIN")" == "N" ];then
  636.                     iptables -N $CHAIN
  637.                 else
  638.                     iptables -F $CHAIN  2> /dev/null
  639.                 fi
  640.  
  641.                 Firewall -D FORWARD -m set --match-set $IOT_GROUP src,dst -j $CHAIN
  642.  
  643.                 if [ "$(Chain_exists "other2wan")" == "Y" ];then
  644.                     Firewall -I FORWARD "$(iptables -nvL FORWARD --line | grep -E -m 1 "other2wan" | awk '{print $1}')" -m set --match-set $IOT_GROUP src,dst -j $CHAIN
  645.                 else
  646.                     Firewall -I FORWARD "$(iptables -nvL FORWARD --line | grep -E "ACCEPT     all.*state RELATED,ESTABLISHED" | awk '{print $1+1}')" -m set --match-set $IOT_GROUP src,dst -j $CHAIN
  647.                 fi
  648.             fi
  649.  
  650.             END_CHAIN="ACCEPT"
  651.             DROP_ACCEPT="DROP"                          # Default for chain
  652.  
  653.             if [ "$TRACK_PORTS"  -eq 1 ];then
  654.                 END_CHAIN="logaccept"
  655.             fi
  656.  
  657.             if [ "$ALLOW_ALL" -eq 1 ];then
  658.                 DROP_ACCEPT="ACCEPT"
  659.             fi
  660.  
  661.             if [ "$2" == "init" ] || [ "$2" == "del" ];then
  662.  
  663.                 if [ "$ACTION" == "-I" ];then
  664.                     if [ -f /jffs/configs/IoT_$IOT_GROUP.apps ] && [ "$APP_RULES" != "noapps" ];then
  665.                         echo -e $cBRED
  666.                         APPSCNT=0
  667.                         APPSCNTBAD=0
  668.                         APPSCNTGOOD=0
  669.                         #while read RULE                            # This old-skool method doesn't read the last line!!! if no LF/CRLF
  670.                         while IFS= read -r RULE || [ -n "$RULE" ]   # v1.03 This POSIX method does correctly read the last line with or without trailing LF/CRLF
  671.                             do
  672.                                 DEBUG="=======================================================================641 $RULE "
  673.                                 if [ -z "$(echo $RULE | grep -vE "^#" | grep .)" ];then
  674.                                     continue
  675.                                     DEBUG="=======================================================================644 "
  676.                                 fi
  677.  
  678.                                 APPSCNT=$((APPSCNT+1))
  679.                                 # Add an apps rule for each IP; remove comment| remove leading/trailing spaces            | separate IPs with ','
  680.                                 SRC="-s "$(echo $IOT_DEVICES | sed 's/#.*$//' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | sed 's/ /,/g')
  681.                                 # If 'onerule' is specified then only add a rule for the group
  682.                                 if [ "$ONE_RULE" == "onerule" ];then
  683.                                     SRC=
  684.                                 fi
  685.  
  686.                                 # Alter the WAN interface to match the local WAN if it was imported from another system
  687.                                 # This should happen regardless of the 'forcewan' directive??? i.e. eth0-->vlan2
  688.                                 if  [ "$FORCEWAN" == "forcewan" ] || [ -n "$(echo $RULE | grep -E "\-o eth0")" ];then
  689.                                     RULE=$(echo $RULE | sed 's/\-o \([a-z]*[0|1|2|3]\) /-o '"$WAN_IF"' /')
  690.  
  691.                                     if [ -z "$(echo $RULE | grep -E "\-o")" ];then
  692.                                         # Explicity add the local outbound WAN interface to the rule
  693.                                         RULE=$RULE" -o "$WAN_IF
  694.                                     fi
  695.                                 fi
  696.  
  697.                                 # Check the chain...in case it has been cloned incorrectly
  698.                                 FCHAIN=$(echo $RULE | sed 's/^.*-A //' | awk '{print $1}')
  699.                                 if [ "$FCHAIN" != "$CHAIN" ];then
  700.                                     SayT "***ERROR Expected Chain '-A "$CHAIN"' mismatch? '"$FCHAIN"' with imported apps rule '"$RULE"'"
  701.                                     echo -e "\a\t***ERROR Expected Chain '-A "$CHAIN"' mismatch? '"$FCHAIN"' with imported apps rule '"$RULE"'"
  702.                                     APPSCNTBAD=$((APPSCNTBAD+1))
  703.                                     continue
  704.                                 fi
  705.  
  706.                                 RESULT=$(Firewall $RULE $SRC)                   # Add the apps rule
  707.  
  708.                                 RC=$?
  709.                                 if [ $RC -ne 0 ];then
  710.                                     echo -e $RESULT
  711.                                     $IPT $RULE
  712.                                     APPSCNTBAD=$((APPSCNTBAD+1))
  713.                                 else
  714.                                     APPSCNTGOOD=$((APPSCNTGOOD+1))
  715.                                 fi
  716.                             done < /jffs/configs/IoT_$IOT_GROUP.apps
  717.                             TXT=
  718.                             if [ $APPSCNTBAD -gt 0 ];then
  719.                                 TXT=${cBRED}$aBLINK"\aFAILED="${APPSCNTBAD}$cRESET
  720.                             fi
  721.                             SayT "Apps firewall rules ("$APPSCNT") read from '/jffs/configs/IoT_$IOT_GROUP.apps' Success="$APPSCNTGOOD $TXT
  722.                             echo -e $cBGRE"\n\tApps firewall rules ("$APPSCNT") read from '/jffs/configs/IoT_$IOT_GROUP.apps' Success="$APPSCNTGOOD $TXT
  723.                             echo -en $cBRED
  724.                     else
  725.                         #Say "*Warning '/jffs/configs/IoT_$IOT_GROUP.apps'"
  726.                         DUMMY=
  727.                     fi
  728.  
  729.                     # Default LOG EVERYTHING?
  730.                     Firewall -A $CHAIN -j LOG --log-prefix "[BAD$IOT_GROUP] " --log-tcp-sequence --log-tcp-options --log-ip-options
  731.  
  732.  
  733.                     # Either Block or Track rule
  734.                     if  [ "$FORCEWAN" == "forcewan" ];then
  735.  
  736.                         Firewall -A $CHAIN -o $WAN_IF -j $DROP_ACCEPT
  737.                     else
  738.                         Firewall -A $CHAIN -j $DROP_ACCEPT
  739.                     fi
  740.  
  741.                 fi
  742.  
  743.  
  744.             fi
  745.  
  746.             # All IoT need to allow NTP (port 123) outbound? - Might be prudent to use router as internal NTPD?
  747.             # NOTE: Allow NTP for ALL LAN devices - reduces number of rules!
  748.             for ACTION in $ACTIONS              # Create rules (but delete them if they already exist to prevent duplicates) or simply Delete if requested!
  749.                 do
  750.                     if [ -n "$LOCALNTP" ] || [ "$BLOCKNTP" -eq 1 ] ;then                # v1.02
  751.                         for DEVICE in $IOT_DEVICES
  752.                             do
  753.                                 if [ -n "$LOCALNTP" ];then                                      # v1.02                            
  754.                                     if [ "$ACTION" == "-I" ];then                   # Redirect NTP to local NTP server
  755.                                         iptables -C PREROUTING -t nat -i br0 -s $DEVICE -p udp -m udp --dport 123 -j DNAT --to-destination $LOCALNTP -m comment --comment "$IOT_GROUP" 2> /dev/null
  756.                                         if [ $? -eq 1 ];then
  757.                                             iptables $ACTION PREROUTING -t nat -i br0 -s $DEVICE -p udp -m udp --dport 123 -j DNAT --to-destination $LOCALNTP -m comment --comment "$IOT_GROUP"
  758.                                         fi
  759.                                     fi
  760.                                 else
  761.                                     if [ "$BLOCKNTP" -eq 1 ];then
  762.                                         #if [ "$ACTION" == "-I" ] && [ "$2" != "init" ];then    # Don't allow NTP if 'init blockntp' requested
  763.                                             Firewall $ACTION $CHAIN $FWRULENO -i br0 -o $WAN_IF -p udp -m udp --dport 123 -j ACCEPT
  764.                                         #fi
  765.                                     fi
  766.                                 fi 
  767.                             done
  768.                     else
  769.                         # Track who is referencing the external NTP server the most
  770.                         if [ "$TRACK_NTP" -eq 0 ];then
  771.                             if [ "$(Chain_exists "$CHAIN")" == "N" ];then
  772.                                 iptables -N $CHAIN
  773.                             fi
  774.                             # Allow OUT=* otherwise if we have Policy Route=ALL then it will fail
  775.                             Firewall $ACTION $CHAIN $FWRULENO -i br0 -p udp -m udp --dport 123 -j ACCEPT
  776.                         else
  777.                             Firewall $ACTION $CHAIN $FWRULENO -i br0 -p udp -m udp --dport 123 -j logaccept
  778.                         fi
  779.                     fi 
  780.                 done
  781.  
  782.             if [ "$2" == "del" ];then
  783.                     Firewall -D FORWARD -m set --match-set $IOT_GROUP src,dst -j $CHAIN
  784.                     Firewall -F $CHAIN  2> /dev/null
  785.                     Firewall -X $CHAIN  2> /dev/null
  786.                     if [ -n "$(iptables -t nat -nvL PREROUTING | grep -E ":123.*$DEVICE.*$IOT_GROUP")" ];then
  787.                         DEVICE_LIST=$(iptables -t nat -nvL PREROUTING | grep -E ":123.*$DEVICE.*$IOT_GROUP" | awk '{print $8}')
  788.                         LOCALNTP=$(iptables -t nat -nvL PREROUTING | grep -E -m 1 "$IOT_GROUP" | awk '{print $NF}' | cut -d':' -f2)
  789.                         for DEVICE in $DEVICE_LIST
  790.                             do         
  791.                                 Firewall -D PREROUTING -t nat -i br0 -s $DEVICE -p udp -m udp --dport 123 -j DNAT --to-destination $LOCALNTP -m comment --comment "$IOT_GROUP" # v1.02
  792.                             done
  793.                     fi
  794.             fi
  795.         done
  796.  
  797.     if [ "$2" == "init" ];then
  798.         echo -e $cBCYA
  799.         Show_Status $IOT_GROUP "IPSET"
  800.     else
  801.         echo -e $cBGRE
  802.         Say "IoT (Group "$IOT_GROUP") Blocking DELETED"
  803.     fi
  804. else
  805.     if [ "$2" == "save" ];then
  806.         echo -e $cBGRE
  807.         Say "Saving IoT (Group "$IOT_GROUP") Blocking config to '"$DIR"/$IOT_GROUP.config'....."
  808.         echo -en $cBRED
  809.         ipset $SAVE $IOT_GROUP           >     $DIR/$IOT_GROUP.config
  810.         echo -e $cRESET
  811.         exit 0
  812.     else
  813.         echo -e $cBWHT
  814.         ShowHelp
  815.         echo -e $cRESET
  816.         exit 0
  817.     fi
  818. fi
  819.  
  820. echo -e $cBWHT
  821. TXT="Blocking"
  822. if [ $ALLOW_ALL -eq 1 ];then
  823.     TXT="Tracking"
  824. fi
  825. Say "IoT (Group "$IOT_GROUP") Firewall" $TXT "complete."
  826. echo -e $cRESET
  827.  
  828.  
  829. #grep -F "10.88.8.126" /opt/var/log/messages  | grep -v "BLOCK" | grep -E "ACCEPT|DROP" | sed 's/SEQ=.*$//;s/LEN=.*DF//;s/LEN=[0-9].*$//'
  830.  
  831.  
  832.  
  833. exit 0
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement