For anyone running services on their home network a Dynamic DNS setup is a must have. But what happens when your Dynamic DNS client fails to update one day, when you’re going on a trip and you end up locked out of your network? If you’re running Nagios as your monitoring solution then you can easily detect this situation. This post will show you how and provide a Nagios plugin for doing just this.
The basic idea is to compare the DNS result for your local network FQDN with your external IP address. To retrieve our external address we use a 3rd party service, which being outside our network can see our external IP. In my case I use ifconfig.co, which conveniently has the ability to return its result in JSON for easy consumption by any number of tools. DNS lookup of our FQDN is provided by the Python socket.gethostbyname function. This gives us too addresses which, if everything is working, will be identical. If our Dynamic DNS client it having issues, the addresses will be different.
Anyway, on to the code (we’re going to need the Python requests module, so install it with pip install requests):
#!/usr/bin/env python3 import requests import socket import sys import argparse STATUS_OK = 0 STATUS_WARNING = 1 STATUS_CRITICAL = 2 STATUS_UNKNOWN = 3 def format_output(status, msg): if status == STATUS_OK: print("OK - {}".format(msg)) elif status == STATUS_WARNING: print("WARNING - {}".format(msg)) elif status == STATUS_CRITICAL: print("CRITICAL - {}".format(msg)) elif status == STATUS_UNKNOWN: print("UNKNOWN - {}".format(msg)) sys.exit(status) def main(): parser = argparse.ArgumentParser(description="Nagios plugin to check that external IP matches DNS IP") parser.add_argument('-H', '--hostname', metavar='HOST', default='', help="DNS hostname to look up") args = parser.parse_args() req = requests.get("https://ifconfig.co/json") if req.status_code != 200: format_output(STATUS_UNKNOWN, "Unable to determine external IP, status code = {}".format(req.status_code)) ext_ip = req.json()['ip'] dns_ip = socket.gethostbyname(args.hostname) if ext_ip != dns_ip: format_output(STATUS_CRITICAL, "DNS IP ({}) does not match external IP ({})".format(dns_ip, ext_ip)) else: format_output(STATUS_OK, "DNS IP correct. External IP is {}".format(ext_ip)) if __name__ == "__main__": main()
This is a fairly basic Nagios plugin that implements the approach described above. The only slightly tricky thing is output formatting and return code conventions, which must be exactly correct for Nagios to interpret the results of your plugin. This convention is documented in the Nagios plugin API documentation (I love this approach as an example of Unixy design).
To use this with nagios, put the plugin in the nagios plugins directory (/usr/local/nagios/libexec/ in my case) and make it executable (chmod +x). Then you need to update your config to add a new command in your objects/commands.cfg file:
# 'check_dynamic_ip' command definition define command { command_name check_dynamic_ip command_line $USER1$/check_dynamic_ip.py -H "$ARG1$" }
You will also need a corresponding service check in your server.cfg file:
define service{ use generic-service host_name hostname service_description Dynamic IP Status check_command check_dynamic_ip!my.domain.com }
Then simply restart Nagios (sudo systemctl restart nagios.service) and you’re done.
Now you can enjoy knowing when your going to be locked out of your network 😉
Leave a Reply