WAN IPv4 checker & email notifier for computers behind dynamic IP/DHCP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
8.8 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. #!/bin/env bash
  2. # WAN IP Checker - Whenever server WAN IP address changes, inform admins via email
  3. # Copyright (C) 2020 Pekka Helenius
  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 <https://www.gnu.org/licenses/>.
  17. ###########################################################
  18. # A script for remote server environments which are behind
  19. # dynamic (non-static) DHCP. Usually these dynamic IPs are
  20. # used in common household networks in non-corporate
  21. # environments.
  22. ###########################################################
  23. # Script requirements
  24. #
  25. # sSMTP
  26. # https://wiki.archlinux.org/index.php/SSMTP
  27. # Relevant conf files
  28. # /etc/ssmtp/revaliases (email conf)
  29. # /etc/ssmtp/ssmtp.conf (email conf)
  30. # /etc/ssmtp/wanchecker.conf (env vars, email conf)
  31. # Because your email password is stored as clear text in /etc/ssmtp/ssmtp.conf, it is important that this file
  32. # is secure. By default, the entire /etc/ssmtp directory is accessible only by root and the mail group.
  33. # The /usr/bin/ssmtp binary runs as the mail group and can read this file. There is no reason to add
  34. # yourself or other users to the mail group.
  35. ###########################################################
  36. ############################
  37. # Fallback DNS
  38. # This service must work even if our system-wide DNS configuration fails
  39. # NOTE: If your WAN firewall blocks external DNS servers, this fallback method fails.
  40. #
  41. # List of overriding fallback DNS servers
  42. #
  43. # CURL:
  44. # Optionally requires curl built with '--enable-dnsshuffle' and '--enable-ares' configure options
  45. #
  46. # Other applications:
  47. # Requires shell preload library '/usr/lib/libresolvconf-override.so' (https://github.com/hadess/resolvconf-override)
  48. #
  49. FALLBACK_DNS=(
  50. # OpenDNS
  51. 208.67.220.222
  52. 208.67.220.220
  53. # Google DNS
  54. 8.8.8.8
  55. 8.8.4.4
  56. # OpenNIC DNS
  57. 58.6.115.42
  58. 58.6.115.43
  59. 119.31.230.42
  60. 200.252.98.162
  61. 217.79.186.148
  62. 81.89.98.6
  63. 78.159.101.37
  64. 203.167.220.153
  65. 82.229.244.191
  66. 216.87.84.211
  67. 66.244.95.20
  68. 207.192.69.155
  69. 72.14.189.120
  70. # Alternate DNS
  71. 198.101.242.72
  72. 23.253.163.53
  73. # FreeDNS
  74. 37.235.1.174
  75. 37.235.1.177
  76. )
  77. ############################
  78. source /etc/ssmtp/wanchecker.conf
  79. ############################
  80. function resolvconfOverrideDNSList {
  81. local i=0
  82. local max_dns=4
  83. local dns_strlist=""
  84. while [[ $i -lt $(( ${#FALLBACK_DNS[@]} - 1)) ]]; do
  85. [[ ${i} == ${max_dns} ]] && break
  86. dns_strlist="${dns_strlist} NAMESERVER$((${i} + 1))=${FALLBACK_DNS[$i]}"
  87. let i++
  88. done
  89. echo "${dns_strlist}"
  90. }
  91. function curlFallBackDNS {
  92. fallback_dns=""
  93. preload_lib=""
  94. if [[ $(curl -V | grep -c -oE '(c-ares|AsynchDNS)') -eq 2 ]] &&
  95. [[ $ENABLE_FALLBACK_DNS == 1 ]]; then
  96. # Fallback DNS servers can be used
  97. fallback_dns=$(echo ${FALLBACK_DNS[*]} | sed 's/ /,/g')
  98. elif [[ -f "/usr/lib/libresolvconf-override.so" ]] && [[ $ENABLE_FALLBACK_DNS == 1 ]]; then
  99. # Curl is built without '--enable-dnsshuffle' and fallback is enabled
  100. preload_lib="LD_PRELOAD=/usr/lib/libresolvconf-override.so $(resolvconfOverrideDNSList)"
  101. fi
  102. if [[ ${fallback_dns} != "" ]]; then
  103. fallback_dns="--dns-servers ${fallback_dns}"
  104. fi
  105. CURL_DNS_LIST=("${preload_lib}" "${fallback_dns}")
  106. }
  107. function getMyIP {
  108. RESOLVERS=(
  109. # Does not work anymore
  110. #"dig +short myip.opendns.com @resolver1.opendns.com"
  111. "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} https://checkip.amazonaws.com"
  112. "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} checkip.dyndns.org"
  113. "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} ifconfig.me"
  114. "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} ipecho.net/plain"
  115. "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} bot.whatismyipaddress.com"
  116. "${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} icanhazip.com"
  117. )
  118. IFS=$'\n'
  119. response=""
  120. for resolver in ${RESOLVERS[@]}; do
  121. check=$(eval "${resolver}" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}")
  122. if [[ ${check} =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
  123. response=${check}
  124. break
  125. fi
  126. done
  127. IFS=' '
  128. }
  129. function getMyCity() {
  130. city=$(eval ${CURL_DNS_LIST[0]} curl -s ${CURL_DNS_LIST[1]} https://json.geoiplookup.io/${response} | awk -F '"' '/city/{ print $(NF-1); }')
  131. }
  132. function checkWANIP {
  133. # Log file timestamp format
  134. local TIMESTAMP=$(date '+%d-%m-%Y,%X')
  135. # Resolve the current IPv4 WAN address
  136. # Attempt with system common DNS resolvers
  137. getMyIP
  138. if [[ ${response} == "" ]]; then
  139. # Fallback to listed DNS resolvers
  140. curlFallBackDNS
  141. getMyIP
  142. fi
  143. local WANIP_CURRENT="${response}"
  144. ############################
  145. # If we are connected to internet...
  146. # There's no point to do WAN IP check if we can't establish connection to WAN/Internet at all
  147. # In addition, do not generate any network related variables if the connection
  148. # can't be established. Therefore, include variable defitions inside this if statement.
  149. if [[ ${response} != "" ]]; then
  150. # Get city information for email, based on fetched WAN IP address
  151. unset CURL_DNS_LIST
  152. # Attempt with system common DNS resolvers
  153. getMyCity
  154. if [[ ${city} == "" ]]; then
  155. # Fallback to listed DNS resolvers
  156. curlFallBackDNS
  157. getMyCity
  158. if [[ ${city} == "" ]]; then
  159. city="Default"
  160. fi
  161. fi
  162. local WANIP_CITY="${city}"
  163. ############################
  164. if [[ ! -d "${WANIP_DIR}" ]]; then
  165. mkdir -p "${WANIP_DIR}"
  166. fi
  167. if [[ ! -f "${WANIP_LOG}" ]] || [[ $(cat "${WANIP_LOG}" | wc -l) == 0 ]]; then
  168. printf "%-25s%-18s%-8s\n" "Time" "WAN IPv4" "Email sent" > "${WANIP_LOG}"
  169. chmod o-r "${WANIP_LOG}"
  170. fi
  171. if [[ $(cat "${WANIP_LOG}" | wc -l) -gt 1 ]] ; then
  172. local WANIP_OLD=$(tail -1 "${WANIP_LOG}" | grep -oE '([0-9]{2,3}\.){3}([0-9]{2,3})')
  173. fi
  174. if [[ ${WANIP_OLD} == "" ]]; then
  175. # Email subject/title
  176. local SUBJECT_EMAIL="New WAN IP address registered (${WANIP_CITY}, ${WANIP_CURRENT})"
  177. # Email message/body contents
  178. local MESSAGE_EMAIL="${TIMESTAMP}: New WAN IP address ${WANIP_CURRENT} has been registered in location ${WANIP_CITY}. Notifier: $(cat /etc/hostname)"
  179. # Message to server stdout
  180. local MESSAGE_STDOUT="${TIMESTAMP} - New WAN IP address ${WANIP_CURRENT} has been registered for this computer"
  181. else
  182. # Email subject/title
  183. local SUBJECT_EMAIL="WAN IP address changed (${WANIP_CITY}, ${WANIP_OLD} -> ${WANIP_CURRENT})"
  184. # Email message/body contents
  185. local MESSAGE_EMAIL="${TIMESTAMP}: WAN IP address ${WANIP_OLD} has been changed to ${WANIP_CURRENT} in location ${WANIP_CITY}. Notifier: $(cat /etc/hostname)"
  186. # Message to server stdout
  187. local MESSAGE_STDOUT="${TIMESTAMP} - WAN IP address of this computer ($(cat /etc/hostname)) has been changed from ${WANIP_OLD} to ${WANIP_CURRENT}"
  188. fi
  189. ############################
  190. function mailSend {
  191. local EMAIL_FORM="To: ${1}\nFrom: ${EMAIL_SENDER}\nSubject: ${SUBJECT_EMAIL}\n\n${MESSAGE_EMAIL}"
  192. echo -e "${EMAIL_FORM}" | ${ssmtp_exec} -v "${1}"
  193. if [[ $? -eq 0 ]]; then
  194. MAIL_SENT="OK"
  195. else
  196. if [[ -f "/usr/lib/libresolvconf-override.so" ]] && [[ $ENABLE_FALLBACK_DNS == 1 ]]; then
  197. SENDMAIL_PRELOAD="LD_PRELOAD=/usr/lib/libresolvconf-override.so $(resolvconfOverrideDNSList)"
  198. echo -e "${EMAIL_FORM}" | $(eval ${SENDMAIL_PRELOAD} ${ssmtp_exec} -v "${1}")
  199. if [[ $? -eq 0 ]]; then
  200. MAIL_SENT="OK"
  201. fi
  202. fi
  203. fi
  204. }
  205. ############################
  206. typeset -A MAIL_SENT_STATUSES
  207. if [[ "${WANIP_OLD}" != "${WANIP_CURRENT}" ]] || \
  208. [[ $(cat "${WANIP_LOG}" | wc -l) -eq 1 ]] ; then
  209. echo -e "${MESSAGE_STDOUT}"
  210. IFS=$' '
  211. retry=4
  212. r=0
  213. for recipient in $EMAIL_RECIPIENTS; do
  214. MAIL_SENT="NOK"
  215. while [[ $r < $retry ]]; do
  216. mailSend "${recipient}"
  217. [[ "${MAIL_SENT}" == "OK" ]] && break
  218. sleep 5
  219. let r++
  220. done
  221. MAIL_SENT_STATUSES+=([${recipient}]="${MAIL_SENT}")
  222. done
  223. IFS=
  224. MAIL_SENT_STATUSES_STR=""
  225. for email in ${!MAIL_SENT_STATUSES[@]}; do
  226. MAIL_SENT_STATUSES_STR="${MAIL_SENT_STATUSES_STR}${email}:${MAIL_SENT_STATUSES[$email]},"
  227. done
  228. MAIL_SENT_STATUSES_STR=$(echo "${MAIL_SENT_STATUSES_STR}" | sed 's/,$//')
  229. printf "%-25s%-18s%s\n" "${TIMESTAMP}" "${WANIP_CURRENT}" "${MAIL_SENT_STATUSES_STR}" >> "${WANIP_LOG}"
  230. fi
  231. fi
  232. }
  233. ############################
  234. checkWANIP