summaryrefslogtreecommitdiff
path: root/ipnotify.sh
blob: 2470fa3118aaa94bf32d27d7f48ba89243e2e6c2 (plain)
    1 #!/usr/bin/env bash
    2 # IPNotify notifies system owner when the system's external IP changes
    3 # Copyright (C) 2018  Aaron Ball <nullspoon@oper.io>
    4 #
    5 # This program is free software: you can redistribute it and/or modify
    6 # it under the terms of the GNU General Public License as published by
    7 # the Free Software Foundation, either version 3 of the License, or
    8 # (at your option) any later version.
    9 #
   10 # This program is distributed in the hope that it will be useful,
   11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
   12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13 # GNU General Public License for more details.
   14 #
   15 # You should have received a copy of the GNU General Public License
   16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17 
   18 
   19 # Write file to tmpfs location. This way the server reports its location on
   20 # boot as the filesystem will be recreated at that time.
   21 STATUSFILE="${STATUSFILE:-/tmp/$(basename ${0})-status}"
   22 
   23 # SMTP connection string in smtp[s]://host:port format. This is used as the
   24 # mail relay to send the report email.
   25 SMTP="${SMTP:-}"
   26 
   27 # Address the email should come from (eg: noreply@somehost.foo)
   28 FROM="${FROM:-}"
   29 
   30 # URI to query for the current external IP.
   31 # NOTE: This URI should only contain the ip address. Any additional text (such
   32 #       as query timestamps) will trigger an email every time the text changes,
   33 #       not just when the returned IP changes.
   34 QUERYURI="${QUERYURI}"
   35 
   36 # Admin email address.
   37 # Can be format user@web.com or "First Last <user@web.com>". Also supports
   38 # comma and semicolon delimited emails for multiple admins.
   39 ADMIN="${ADMIN:-}"
   40 
   41 
   42 titlize() {
   43   local text="${@}"
   44 
   45   printf -- "%s\n" "${text}"
   46   printf -- '-%.0s' $(seq 1 ${#text})
   47   printf -- '\n'
   48 }
   49 
   50 
   51 prereq_verify() {
   52   # Required binaries:
   53   #   curl
   54   #   mailx
   55   #   traceroute
   56 
   57   # Validate GNU traceroute installation
   58   if [ -z "$(type -p traceroute)" ]; then
   59     printf "ERROR: GNU traceroute is required but could not be found.\n" >&2
   60     return 1
   61   fi
   62   if [ -z "$(traceroute -V | grep 'GNU inetutils')" ]; then
   63     printf "ERROR: GNU traceroute is required but another version is installed.\n" >&2
   64     return 1
   65   fi
   66 
   67   if [ -z "$(type -p curl)" ]; then
   68     printf "ERROR: Curl required but could not be found.\n" >&2
   69     return 1
   70   fi
   71 
   72   if [ -z "$(type -p mailx)" ]; then
   73     printf "ERROR: mailx is required but could not be found.\n" >&2
   74     return 1
   75   fi
   76 
   77   return 0
   78 }
   79 
   80 
   81 notify_user() {
   82   local admin="${1}"
   83   local newip="${2}"
   84   local smtp="${3}"
   85   local from="${4}"
   86 
   87   local email_domain=$(echo ${admin} | cut -d '@' -f 2)
   88 
   89   # Construct email body
   90   printf "To: ${admin}
   91 Subject: [report] Server $(hostname) has changed IPs
   92 
   93 
   94 This server, $(hostname), has changed IPS.
   95 
   96 $(titlize New IP)
   97 
   98   ${newip}
   99 
  100 
  101 $(titlize "ICMP Traceroute to ${email_domain}")
  102 $(timeout 12 traceroute --type=icmp -m 20 -w 2 ${email_domain} 2>&1 | sed 's/^/  /')
  103 
  104 
  105 $(titlize "Resolv.conf")
  106 $(cat /etc/resolv.conf | sed 's/^/  /')
  107 
  108 
  109 " | timeout 14 mailx -S "smtp=${smtp}" -r "${from}" -t "${admin}"
  110 
  111   return $?
  112 }
  113 
  114 
  115 validate_vars() {
  116   if [ -z "${QUERY}" ]; then
  117     printf "QUERY endpoint unset. Cannot determine external IP.\n" >&2
  118     return 1
  119   fi
  120 
  121   if [ -z "${SMTP}" ]; then
  122     printf "SMTP relay server unset (eg: SMTP=smtp://hostname:587)\n" >&2
  123     return 1
  124   fi
  125 
  126   if [ -z "${FROM}" ]; then
  127     printf "FROM address unset.\n" >&2
  128     return 1
  129   fi
  130 
  131   if [ -z "${ADMIN}" ]; then
  132     printf "ADMIN unset. No one to notify (eg: ADMIN=admin@website.com).\n" >&2
  133     return 1
  134   fi
  135 }
  136 
  137 
  138 main() {
  139   local config="${1:-}" # Config file containing global variables
  140   local curip           # Placeholder for current ip address (as returned by
  141                         # the query destination)
  142   local cacheip         # Cached ip address (if cache file exists)
  143 
  144 
  145   if [ -z "${config}" ]; then
  146     printf "Config file required\n" >&2
  147     return 1
  148   fi
  149 
  150   # Source the config file to load its variables
  151   source "${config}"
  152 
  153   # Validate all required programs are present (eg: mailx, traceroute, ping, etc)
  154   prereq_verify
  155   [ $? -gt 0 ] && return 1
  156 
  157   # Validate all required variables are set (eg: FROM, ADMIN, etc)
  158   validate_vars
  159   [ $? -gt 0 ] && return 1
  160 
  161 
  162   # Get the current real ip
  163   curip="$(curl --max-time 7 --location --insecure --silent ${QUERY})"
  164 
  165   if [ -z ${curip:-} ]; then
  166     printf "Current ip unknown. Cannot continue\n"
  167     return 2
  168   fi
  169 
  170   # Set ip to state file if none is found
  171   if [ ! -f "${STATUSFILE}" ]; then
  172     printf "IP status file not found. Creating\n"
  173     printf "${curip}" > "${STATUSFILE}"
  174     notify_user "${ADMIN}" "${curip}" "${SMTP}" "${FROM}"
  175     return 0
  176   fi
  177 
  178   cacheip="$(cat ${STATUSFILE})"
  179 
  180   # Cached ip and current ip don't match - email admin
  181   if [ "${cacheip:-}" != "${curip:-}" ]; then
  182     printf "Current ip does not match last recorded IP. Notifying admin.\n"
  183     notify_user "${ADMIN}" "${curip}" "${SMTP}" "${FROM}"
  184 
  185     if [ $? -eq 0 ]; then
  186       printf "${curip}" > "${STATUSFILE}"
  187       return 0
  188     fi
  189     printf "Could not send notification email.\n"
  190     printf "Something is probably still wrong. Skipping write of IP\n"
  191     return 1
  192   fi
  193 
  194   printf "Nothing to see here\n"
  195 }
  196 
  197 main ${@}

Generated by cgit