Watching my DNS records
Update 2020-11-17: I forced
curl
to use IPv4, as I sometimes got IPv6 back. And I addedDate
andMessage-ID
headers to email, as this reduces the SpamAssassin score (I tested with this).
Home routers and ISPs are not always the best for hosting a website. One reason in particular, is the lack of static IP-addresses. So whenever my router restarts, I risk getting a new IP. Which in turn means the DNS records for this site (krex.no) become invalid! I could enable ddns, but my router is behind NAT. (Yes, double NAT is bad, but I will clean that up at a later time).
So to fix this, I wanted to know when my IP has changed. And when it does, I want to get an email. Later on, I can update the DNS records automatically. My registrar has both and API and API clients available. But for now, I just want to know. The automatic DNS record updates can be dealt with later, when I get tired of manually updating them.
🔗 Sub-problems
Our problem has 3 parts:
- What IP does the DNS say we have?
- What IP do we actually have? (The dynamic, public IP)
- How do we email ourselves?
🔗 The scripts
So first off, SSH into the server and create a folder: ip-watch
.
🔗 Getting our IP
A guy at cloudflare made canhazip.com to easily get your ip. Put this into current-ip.sh
:
#!/bin/bash
curl --silent -4 canhazip.com
The -4
tells curl to use IPv4. Other alternatives:
dig +short TXT o-o.myaddr.l.google.com @ns3.google.com
nslookup myip.opendns.com resolver1.opendns.com
However they might require some grep
ing.
🔗 Getting the DNS ip
Some like dig
, some like nslookup
, but neither are pre-installed on my raspberry pi. Instead, we opt for getent hosts
. Put this into current-dns.sh
:
#!/bin/bash
getent hosts krex.no | awk '{ print $1 }'
🔗 Sending the emails
I already set up postfix in docker using boky/postfix with some tweaks to make it run on a 32bit os. This adds a SMTP server on my localhost on port 25 and 587 (TLS). When I send email through this server, it can use OpenDKIM when sending the email, and this avoid some spam filters such as gmail's. Here is my signature.
To stitch the scripts together, I intend to make another script. So the DNS IP and current IP are sent from the command line. Put this in email-warning.py
:
#!/usr/bin/env python3
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import email.utils
from datetime import datetime
from sys import argv
def send_email(dns_ip, current_ip):
with smtplib.SMTP(host='localhost', port=587) as s:
msg = MIMEMultipart()
body = f"DNS has ip {dns_ip}\nThe current ip is {current_ip}\n\nSent at {datetime.now()}."
msg["From"] = "DNS IP Watcher <noreply@krex.no>"
msg["To"] = "INSERT-YOUR-EMAIL-HERE@email.com" # Change this line
msg["Subject"] = "[krex.no] DNS is invalid " + str(datetime.now())
msg["Date"] = email.utils.formatdate(localtime=True)
msg["Message-ID"] = email.utils.make_msgid(domain="krex.no")
msg.attach(MIMEText(body, 'plain'))
s.send_message(msg)
print("Sent message '%s' to %s. Message-ID: %s" % (msg["Subject"], msg["To"], msg["Message-ID"]))
def main():
if len(argv) != 3:
print(f"Usage: {argv[0]} dns_ip current_ip")
exit(1)
dns_ip = argv[1]
current_ip = argv[2]
if dns_ip != current_ip:
send_email(dns_ip, current_ip)
if __name__ == '__main__':
main()
🔗 The final glue
The script which we will run with cron
is called do-check.sh
:
#!/bin/bash
DNS=$(./current-dns.sh)
IP=$(./current-ip.sh)
echo "Current DNS: $DNS"
echo "Current IP: $IP"
exec ./email-warning.py $DNS $IP
Don't forget to make the files executable:
chmod +x *.sh *.py
🔗 Running it daily
cron
has been mentioned already, and is perfect for this. Run crontab -e
to edit the file, and add a new line:
# DNS/IP test every day at 13:00
0 13 * * * /home/pi/dev/ip-watch/do-check.sh