diff --git a/ssmtp_conf-sample/wanchecker.sh b/ssmtp_conf-sample/wanchecker.sh index 7ea49c5..027b1a0 100755 --- a/ssmtp_conf-sample/wanchecker.sh +++ b/ssmtp_conf-sample/wanchecker.sh @@ -1,7 +1,7 @@ #!/bin/env bash # WAN IP Checker - Whenever server WAN IP address changes, inform admins via email -# Copyright (C) 2019 Pekka Helenius +# Copyright (C) 2020 Pekka Helenius # # 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 @@ -31,44 +31,143 @@ # https://wiki.archlinux.org/index.php/SSMTP # Relevant conf files -# /etc/ssmtp/revaliases -# /etc/ssmtp/ssmtp.conf +# /etc/ssmtp/revaliases (email conf) +# /etc/ssmtp/ssmtp.conf (email conf) +# /etc/ssmtp/wanchecker.conf (env vars, email conf) -# Because your email password is stored as cleartext in /etc/ssmtp/ssmtp.conf, it is important that this file +# Because your email password is stored as clear text in /etc/ssmtp/ssmtp.conf, it is important that this file # is secure. By default, the entire /etc/ssmtp directory is accessible only by root and the mail group. # The /usr/bin/ssmtp binary runs as the mail group and can read this file. There is no reason to add # yourself or other users to the mail group. ########################################################### -# Some lines below are commented out because the timer is handled by systemd service file -# If you don't use provided systemd service file, re-enable the relevant lines below +############################ +# Fallback DNS -function checkWANIP { +# This service must work even if our system-wide DNS configuration fails +# NOTE: If your WAN firewall blocks external DNS servers, this fallback method fails. +# +# List of overriding fallback DNS servers +# +# CURL: +# Optionally requires curl built with '--enable-dnsshuffle' and '--enable-ares' configure options +# +# Other applications: +# Requires shell preload library '/usr/lib/libresolvconf-override.so' (https://github.com/hadess/resolvconf-override) +# +FALLBACK_DNS=( + # OpenDNS + 208.67.220.222 + 208.67.220.220 + # Google DNS + 8.8.8.8 + 8.8.4.4 + # OpenNIC DNS + 58.6.115.42 + 58.6.115.43 + 119.31.230.42 + 200.252.98.162 + 217.79.186.148 + 81.89.98.6 + 78.159.101.37 + 203.167.220.153 + 82.229.244.191 + 216.87.84.211 + 66.244.95.20 + 207.192.69.155 + 72.14.189.120 + # Alternate DNS + 198.101.242.72 + 23.253.163.53 + # FreeDNS + 37.235.1.174 + 37.235.1.177 +) - # Command to resolve the current IPv4 WAN address - local WANIP_CURRENT="dig +short myip.opendns.com @resolver1.opendns.com" +############################ - # Log file timestamp format - local TIMESTAMP=$(date '+%d-%m-%Y, %X') +source /etc/ssmtp/wanchecker.conf ############################ - # Email sender - local EMAIL_SENDER="mailsender@foo.com" +function resolvconfOverrideDNSList { + local i=0 + local max_dns=4 + local dns_strlist="" + while [[ $i -lt $(( ${#FALLBACK_DNS[@]} - 1)) ]]; do + [[ ${i} == ${max_dns} ]] && break + dns_strlist="${dns_strlist} NAMESERVER$((${i} + 1))=${FALLBACK_DNS[$i]}" + let i++ + done + echo "${dns_strlist}" +} + +function curlFallBackDNS { + + fallback_dns="" + preload_lib="" + if [[ $(curl -V | sed -n '/AsynchDNS/p' | wc -l) -ne 0 ]] && + [[ $ENABLE_FALLBACK_DNS == 1 ]]; then + # Fallback DNS servers can be used + fallback_dns=$(echo ${FALLBACK_DNS[*]} | sed 's/ /,/g') + elif [[ -f "/usr/lib/libresolvconf-override.so" ]] && [[ $ENABLE_FALLBACK_DNS == 1 ]]; then + # Curl is built without '--enable-dnsshuffle' and fallback is enabled + preload_lib="LD_PRELOAD=/usr/lib/libresolvconf-override.so $(resolvconfOverrideDNSList)" + fi + + if [[ ${fallback_dns} != "" ]]; then + fallback_dns="--dns-servers ${fallback_dns}" + fi + + CURL_DNS_LIST=("${preload_lib}" "${fallback_dns}") +} - # Emails to send notification to - local EMAIL_RECIPIENTS=( - "whogetsthemail_1@foo.com" - "whogetsthemail_2@bar.com" +function getMyIP { + + RESOLVERS=( + # Does not work anymore + #"dig +short myip.opendns.com @resolver1.opendns.com" + "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} https://checkip.amazonaws.com" + "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} checkip.dyndns.org" + "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} ifconfig.me" + "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} ipecho.net/plain" + "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} bot.whatismyipaddress.com" + "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} icanhazip.com" ) -############################ + IFS=$'\n' + response="" + for resolver in ${RESOLVERS[@]}; do + check=$(eval "${resolver}" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") + if [[ ${check} =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then + response=${check} + break + fi + done + IFS=' ' - # Email send function - function mailSend { - echo -e "To: ${1}\nFrom: ${EMAIL_SENDER}\nSubject: ${SUBJECT_EMAIL}\n\n${MESSAGE_EMAIL}" | sendmail -v "${1}" - } +} + +function getMyCity() { + city=$(eval ${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} https://json.geoiplookup.io/${response} | awk -F '"' '/city/{ print $(NF-1); }') +} + +function checkWANIP { + + # Log file timestamp format + local TIMESTAMP=$(date '+%d-%m-%Y,%X') + + # Resolve the current IPv4 WAN address + # Attempt with system common DNS resolvers + getMyIP + if [[ ${response} == "" ]]; then + # Fallback to listed DNS resolvers + curlFallBackDNS + getMyIP + fi + + local WANIP_CURRENT="${response}" ############################ @@ -76,50 +175,118 @@ function checkWANIP { # There's no point to do WAN IP check if we can't establish connection to WAN/Internet at all # In addition, do not generate any network related variables if the connection # can't be established. Therefore, include variable defitions inside this if statement. - if [[ $(printf $(eval "${WANIP_CURRENT}" &> /dev/null)$?) -eq 0 ]]; then - -############################ + if [[ ${response} != "" ]]; then + + # Get city information for email, based on fetched WAN IP address + unset CURL_DNS_LIST + # Attempt with system common DNS resolvers + getMyCity + if [[ ${city} == "" ]]; then + # Fallback to listed DNS resolvers + curlFallBackDNS + getMyCity + if [[ ${city} == "" ]]; then + city="Default" + fi + fi - # Cache/Log directory of the script - local WANIP_DIR="/var/spool/mail" + local WANIP_CITY="${city}" - # Log file for checked/resolved IPv4 WAN addresses - local WANIP_LOG="$WANIP_DIR/ip_wan.log" +############################ if [[ ! -d "${WANIP_DIR}" ]]; then mkdir -p "${WANIP_DIR}" fi - if [[ ! -f "${WANIP_LOG}" ]]; then - printf 'Time\t\t\t\tWAN IPv4\n' > "${WANIP_LOG}" + if [[ ! -f "${WANIP_LOG}" ]] || [[ $(cat "${WANIP_LOG}" | wc -l) == 0 ]]; then + printf "%-25s%-18s%-8s\n" "Time" "WAN IPv4" "Email sent" > "${WANIP_LOG}" + chmod o-r "${WANIP_LOG}" fi - # Email subject/title - local SUBJECT_EMAIL="WAN IP address changed (Helsinki, $(tail -1 ${WANIP_LOG} | awk '{print $NF}') -> $(eval ${WANIP_CURRENT}))" + if [[ $(cat "${WANIP_LOG}" | wc -l) -gt 1 ]] ; then + local WANIP_OLD=$(tail -1 "${WANIP_LOG}" | awk '{print $2}') + fi + + if [[ ${WANIP_OLD} == "" ]]; then + + # Email subject/title + local SUBJECT_EMAIL="New WAN IP address registered (${WANIP_CITY}, ${WANIP_CURRENT})" + + # Email message/body contents + local MESSAGE_EMAIL="${TIMESTAMP}: New WAN IP address ${WANIP_CURRENT} has been registered in location ${WANIP_CITY}. Notifier: $(cat /etc/hostname)" + + # Message to server stdout + local MESSAGE_STDOUT="${TIMESTAMP} - New WAN IP address ${WANIP_CURRENT} has been registered for this computer" + + else + + # Email subject/title + local SUBJECT_EMAIL="WAN IP address changed (${WANIP_CITY}, ${WANIP_OLD} -> ${WANIP_CURRENT})" - # Email message/body contents - local MESSAGE_EMAIL="${TIMESTAMP}: WAN address of location (Helsinki) has been changed from $(tail -1 ${WANIP_LOG} | awk '{print $NF}') to $(eval ${WANIP_CURRENT})" + # Email message/body contents + local MESSAGE_EMAIL="${TIMESTAMP}: WAN IP address ${WANIP_OLD} has been changed to ${WANIP_CURRENT} in location ${WANIP_CITY}. Notifier: $(cat /etc/hostname)" - # Message to server stdout - local MESSAGE_STDOUT="$(echo ${TIMESTAMP}) - WAN address of this server has been changed from $(tail -1 ${WANIP_LOG} | awk '{print $NF}') to $(eval ${WANIP_CURRENT})" + # Message to server stdout + local MESSAGE_STDOUT="${TIMESTAMP} - WAN IP address of this computer ($(cat /etc/hostname)) has been changed from ${WANIP_OLD} to ${WANIP_CURRENT}" + + fi ############################ - # Log write command - local LOG_WRITE=$(printf '%s %s\t\t%s\n' $(echo "${TIMESTAMP}") $(eval "${WANIP_CURRENT}") >> "${WANIP_LOG}") + function mailSend { + + local EMAIL_FORM="To: ${1}\nFrom: ${EMAIL_SENDER}\nSubject: ${SUBJECT_EMAIL}\n\n${MESSAGE_EMAIL}" + + echo -e "${EMAIL_FORM}" | sendmail -v "${1}" + if [[ $? -eq 0 ]]; then + MAIL_SENT="OK" + else + if [[ -f "/usr/lib/libresolvconf-override.so" ]] && [[ $ENABLE_FALLBACK_DNS == 1 ]]; then + SENDMAIL_PRELOAD="LD_PRELOAD=/usr/lib/libresolvconf-override.so $(resolvconfOverrideDNSList)" + echo -e "${EMAIL_FORM}" | $(eval ${SENDMAIL_PRELOAD} sendmail -v "${1}") + if [[ $? -eq 0 ]]; then + MAIL_SENT="OK" + fi + fi + fi + + } ############################ - if [[ $(tail -1 "${WANIP_LOG}" | awk '{print $NF}') != $(printf '%s' $(eval "${WANIP_CURRENT}")) ]] || \ - [[ $(cat "${WANIP_LOG}" | wc -l) -le 2 ]] ; then + typeset -A MAIL_SENT_STATUSES + + if [[ "${WANIP_OLD}" != "${WANIP_CURRENT}" ]] || \ + [[ $(cat "${WANIP_LOG}" | wc -l) -eq 1 ]] ; then echo -e "${MESSAGE_STDOUT}" - for i in "${EMAIL_RECIPIENTS[@]}"; do - mailSend "${i}" - $LOG_WRITE + IFS=$' ' + retry=4 + r=0 + for recipient in $EMAIL_RECIPIENTS; do + MAIL_SENT="NOK" + while [[ $r < $retry ]]; do + mailSend "${recipient}" + [[ "${MAIL_SENT}" == "OK" ]] && break + sleep 5 + let r++ + done + MAIL_SENT_STATUSES+=([${recipient}]="${MAIL_SENT}") + done + IFS= + + MAIL_SENT_STATUSES_STR="" + for status in ${MAIL_SENT_STATUSES[@]}; do + for email in ${!MAIL_SENT_STATUSES[@]}; do + MAIL_SENT_STATUSES_STR="${MAIL_SENT_STATUSES_STR}${email}:${status}," + done done + MAIL_SENT_STATUSES_STR=$(echo "${MAIL_SENT_STATUSES_STR}" | sed 's/,$//') + + printf "%-25s%-18s%s\n" "${TIMESTAMP}" "${WANIP_CURRENT}" "${MAIL_SENT_STATUSES_STR}" >> "${WANIP_LOG}" + fi fi