diff options
author | Aaron Ball <nullspoon@oper.io> | 2018-06-27 22:01:43 -0600 |
---|---|---|
committer | Aaron Ball <nullspoon@oper.io> | 2018-06-27 22:01:43 -0600 |
commit | 2e6523fb1ef631299a8e3b32145a8390b6bf2b9f (patch) | |
tree | 9c5414bf22a6583ab8612d760e3eb1bbd4a8d31d /ipnotify.sh | |
download | ipnotify-master.tar.gz ipnotify-master.tar.xz |
This version works. It will notify the admin of a system if its external
IP changes. The email report contains new external ip, hostname,
icmp traceroute to the specified query server, and the current
resolv.conf file.
Also validates that all required software is installed and that all
required variables are set.
Diffstat (limited to 'ipnotify.sh')
-rwxr-xr-x | ipnotify.sh | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/ipnotify.sh b/ipnotify.sh new file mode 100755 index 0000000..2470fa3 --- /dev/null +++ b/ipnotify.sh @@ -0,0 +1,197 @@ +#!/usr/bin/env bash +# IPNotify notifies system owner when the system's external IP changes +# Copyright (C) 2018 Aaron Ball <nullspoon@oper.io> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + + +# Write file to tmpfs location. This way the server reports its location on +# boot as the filesystem will be recreated at that time. +STATUSFILE="${STATUSFILE:-/tmp/$(basename ${0})-status}" + +# SMTP connection string in smtp[s]://host:port format. This is used as the +# mail relay to send the report email. +SMTP="${SMTP:-}" + +# Address the email should come from (eg: noreply@somehost.foo) +FROM="${FROM:-}" + +# URI to query for the current external IP. +# NOTE: This URI should only contain the ip address. Any additional text (such +# as query timestamps) will trigger an email every time the text changes, +# not just when the returned IP changes. +QUERYURI="${QUERYURI}" + +# Admin email address. +# Can be format user@web.com or "First Last <user@web.com>". Also supports +# comma and semicolon delimited emails for multiple admins. +ADMIN="${ADMIN:-}" + + +titlize() { + local text="${@}" + + printf -- "%s\n" "${text}" + printf -- '-%.0s' $(seq 1 ${#text}) + printf -- '\n' +} + + +prereq_verify() { + # Required binaries: + # curl + # mailx + # traceroute + + # Validate GNU traceroute installation + if [ -z "$(type -p traceroute)" ]; then + printf "ERROR: GNU traceroute is required but could not be found.\n" >&2 + return 1 + fi + if [ -z "$(traceroute -V | grep 'GNU inetutils')" ]; then + printf "ERROR: GNU traceroute is required but another version is installed.\n" >&2 + return 1 + fi + + if [ -z "$(type -p curl)" ]; then + printf "ERROR: Curl required but could not be found.\n" >&2 + return 1 + fi + + if [ -z "$(type -p mailx)" ]; then + printf "ERROR: mailx is required but could not be found.\n" >&2 + return 1 + fi + + return 0 +} + + +notify_user() { + local admin="${1}" + local newip="${2}" + local smtp="${3}" + local from="${4}" + + local email_domain=$(echo ${admin} | cut -d '@' -f 2) + + # Construct email body + printf "To: ${admin} +Subject: [report] Server $(hostname) has changed IPs + + +This server, $(hostname), has changed IPS. + +$(titlize New IP) + + ${newip} + + +$(titlize "ICMP Traceroute to ${email_domain}") +$(timeout 12 traceroute --type=icmp -m 20 -w 2 ${email_domain} 2>&1 | sed 's/^/ /') + + +$(titlize "Resolv.conf") +$(cat /etc/resolv.conf | sed 's/^/ /') + + +" | timeout 14 mailx -S "smtp=${smtp}" -r "${from}" -t "${admin}" + + return $? +} + + +validate_vars() { + if [ -z "${QUERY}" ]; then + printf "QUERY endpoint unset. Cannot determine external IP.\n" >&2 + return 1 + fi + + if [ -z "${SMTP}" ]; then + printf "SMTP relay server unset (eg: SMTP=smtp://hostname:587)\n" >&2 + return 1 + fi + + if [ -z "${FROM}" ]; then + printf "FROM address unset.\n" >&2 + return 1 + fi + + if [ -z "${ADMIN}" ]; then + printf "ADMIN unset. No one to notify (eg: ADMIN=admin@website.com).\n" >&2 + return 1 + fi +} + + +main() { + local config="${1:-}" # Config file containing global variables + local curip # Placeholder for current ip address (as returned by + # the query destination) + local cacheip # Cached ip address (if cache file exists) + + + if [ -z "${config}" ]; then + printf "Config file required\n" >&2 + return 1 + fi + + # Source the config file to load its variables + source "${config}" + + # Validate all required programs are present (eg: mailx, traceroute, ping, etc) + prereq_verify + [ $? -gt 0 ] && return 1 + + # Validate all required variables are set (eg: FROM, ADMIN, etc) + validate_vars + [ $? -gt 0 ] && return 1 + + + # Get the current real ip + curip="$(curl --max-time 7 --location --insecure --silent ${QUERY})" + + if [ -z ${curip:-} ]; then + printf "Current ip unknown. Cannot continue\n" + return 2 + fi + + # Set ip to state file if none is found + if [ ! -f "${STATUSFILE}" ]; then + printf "IP status file not found. Creating\n" + printf "${curip}" > "${STATUSFILE}" + notify_user "${ADMIN}" "${curip}" "${SMTP}" "${FROM}" + return 0 + fi + + cacheip="$(cat ${STATUSFILE})" + + # Cached ip and current ip don't match - email admin + if [ "${cacheip:-}" != "${curip:-}" ]; then + printf "Current ip does not match last recorded IP. Notifying admin.\n" + notify_user "${ADMIN}" "${curip}" "${SMTP}" "${FROM}" + + if [ $? -eq 0 ]; then + printf "${curip}" > "${STATUSFILE}" + return 0 + fi + printf "Could not send notification email.\n" + printf "Something is probably still wrong. Skipping write of IP\n" + return 1 + fi + + printf "Nothing to see here\n" +} + +main ${@} |