WirelessPhreak.com

I like to travel, f*ck with technology, and partake in the occasional tropical drink.
I am also a co-host on The NBD Show podcast.
Follow Me
Showing posts with label automation. Show all posts
Showing posts with label automation. Show all posts

I two cloud servers i l ike to tinker with, and like most people who have been burned by it, I lock SSH down. No wide-open port 22 for the whole internet to hammer on — my firewall only allows connections from my home IP address. It's a simple rule, and for a long time it worked perfectly.

Then two things happened at once: I started traveling more, and my ISP quietly stopped giving me a static WAN IP.

The traveling part is just a fact of life. When I'm on the road I'm coming in through my home network using a VPN.

The second problem caught me off guard. Over the last year or so my ISP started rotating my WAN IP regularly. Not constantly, but enough that every couple of weeks I'd sit down at my home desk, try to SSH into one of my servers, and get nothing. Dead silence. I'd have to pull up the cloud console, find the old firewall rule, delete it, look up my current WAN IP, add the new rule, and finally get back to work.

After doing this dance one too many times, I decided there had to be a better way.


The Solution: Dynamic DNS + a Nightly Script

The approach I landed on uses two things that, separately, most sysadmins already know about:

Dynamic DNS (DDNS) — A service that keeps a hostname pointed at your current home IP, even as it changes. I'm using DuckDNS, which is free and dead simple. You install a small updater on your home router or a machine on your home network, and it phones home to DuckDNS every few minutes. My test hostname yourhome.duckdns.org will always resolves to whatever my home's current WAN IP is.

A Python script running on each cloud server — Every night, the script resolves that hostname, compares the result to the IP it used last time, and if the IP has changed, it removes the stale UFW rule and adds a fresh one. No console, no manual steps, no cold coffee.


Setting Up Dynamic DNS

If you don't already have a DDNS hostname pointing at your home network, this is the first step.

I use DuckDNS because it's free, reliable, and has clear setup docs for every platform. Head to duckdns.org, sign in with a Google or GitHub account, and claim a subdomain — something like yourname.duckdns.org.

From there, install the DuckDNS updater on your home router or on any machine that stays on at home. Most routers support DDNS natively under their WAN settings — if yours does, point it at DuckDNS and you're done. If not, DuckDNS provides a small shell script you can run as a cron job on any Linux machine on your home network.

Once it's configured, your hostname will always resolve to your current home WAN IP, usually within a few minutes of it changing.


The Python Script

With DDNS in place, the next piece is a Python script that runs on your cloud server and keeps UFW in sync. I asked an AI assistant to write this for me, gave it a clear description of the problem, and it came back with exactly what I needed, no third-party libraries required, just Python's standard library plus a few subprocess calls to ufw.

Here's what the script does, step by step:

  1. Resolve the hostname. It calls socket.getaddrinfo() to look up the current IP for your DDNS hostname.
  2. Compare to the last known IP. The script saves the IP it used last time to a state file at /var/lib/dns_ufw_sync/last_ip.txt. On each run it reads that file and compares.
  3. If the IP changed, update the firewall. It queries ufw status numbered to find and delete the old rule, then adds a new ufw allow rule for the fresh IP.
  4. Reload UFW so the new rule takes effect immediately.
  5. Write the new IP to the state file, ready for the next run.

The script also supports a --dry-run flag that prints exactly what it would do without touching anything its great for testing before you let it loose on a production server.

The key invocation looks like this:

sudo python3 /opt/scripts/dns_ufw_sync.py \
    --hostname yourhome.duckdns.org \
    --port 22 \
    --proto tcp \
    --direction in

Deploying the Script

Once you have the script on your server, setup is three quick steps.

Copy it somewhere sensible and make it executable:

sudo mkdir -p /opt/scripts
sudo cp dns_ufw_sync.py /opt/scripts/dns_ufw_sync.py
sudo chmod +x /opt/scripts/dns_ufw_sync.py

Do a dry run first to make sure everything looks right:

sudo python3 /opt/scripts/dns_ufw_sync.py \
    --hostname yourhome.duckdns.org \
    --port 22 \
    --dry-run

You should see log output showing the resolved IP and what rules would be added or removed. If it looks good, run it once for real (without --dry-run) to create the initial rule and state file.

Schedule it in cron:

sudo crontab -e

Add this line to run it at 2 AM every night:

0 2 * * * /usr/bin/python3 /opt/scripts/dns_ufw_sync.py --hostname yourhome.duckdns.org --port 22 --proto tcp --direction in >> /var/log/dns_ufw_sync.log 2>&1

That's it. The script runs nightly, checks whether your home IP has drifted, and quietly keeps your firewall up to date.


A Few Gotchas Worth Mentioning

The script needs to run as root. UFW requires root privileges to modify rules. Always use sudo crontab -e (root's crontab), not your user's crontab.

2 AM is a convention, not a requirement. Pick any time that makes sense for your situation. If your ISP tends to rotate your IP at predictable times, schedule the script a little after that window.

The script tracks one hostname per instance. If you need to allow multiple DDNS sources — say, a second location you frequently work from — just add a second cron entry pointing at a different hostname and a different state file using the --state-file flag.

Check the log occasionally. Every run is logged to /var/log/dns_ufw_sync.log. Worth a quick glance now and then to confirm DNS resolution is succeeding and rules are being applied as expected.

This doesn't help while you're actively traveling. The script keeps your home IP current in the firewall it won't let you in from a hotel. For that you still need a VPN, a jump host, or cloud console access as a fallback. What it solves is coming home after a trip and finding yourself locked out because your ISP shuffled your IP while you were gone.


Wrapping Up

This was one of those satisfying fixes where the solution ended up being much simpler than the ongoing pain it replaced. A few hours of thinking, a conversation with an AI to write the actual script, and a single cron entry.

If you're in a similar situation locking cloud servers down to a home IP, but dealing with an ISP that won't keep that IP stable this approach is worth the small setup time. Dynamic DNS has been around forever and it's rock solid. Pairing it with a script that closes the loop on the firewall side turns what used to be a recurring manual chore into a fully automated background process.

The full script is linked in this git repository. Feel free to adapt it to your own setup — and if you run into any issues or have improvements, drop them in the comments below.

 

https://github.com/wirelessphreak/dns-ufw-sync