30 python lines Dynamic DNS

Here in Spain, I chose Movistar as my Internet provider, I must say I’m pretty happy with it, symmetric 300Mbps fiber optics and good service. The only annoying aspect is that they do not provide static IP for free, something I was used to and was very convenient.

In order to reach my network from places where I can’t connect to my VPN, I wrote a very simple Dynamic DNS system using dnspython, and it turned out to be fairly easy.

This is the code running on my public server:

$ cat ddns.py
from flask import Flask

import dns.update
import dns.query
import dns.tsigkeyring
import dns.resolver

app = Flask(__name__)

def query(t, fqdn):
    resolver = dns.resolver.Resolver(configure=False)
    resolver.nameservers = ['']
    answer = dns.resolver.query(fqdn, t)
    if t == 'a':
        return '{0}\n'.format(answer.rrset[0].address)

@app.route("/update/<domain>/<host>/<ip>", methods=['POST'])
def update(domain, host, ip):

    keyring = dns.tsigkeyring.from_text({
        'rndc-key' : 'myRNDCkey=='

    update = dns.update.Update('{0}.'.format(domain), keyring=keyring)
    update.replace(host, 300, 'A', ip)
    response = dns.query.tcp(update, '', timeout=10)

    return "update with {0}\n".format(ip)

if __name__ == "__main__":

This code is served by gunicorn:

$ gunicorn ddns:app 

Behind an nginx reverse proxy:

location /dyndns/ {
    auth_basic "booh.";
    auth_basic_user_file /etc/nginx/htpasswd;
    proxy_pass http://private-server:8000/;

On the client side (home), I wrote this very simple shell script:




# here retrieve your actual public IP from a website like httpbin.org
curip=$(curl -s -o- http://some.website.like.httpbin.org/ip)
# fetch recorded IP address
homeip=$(curl -u ${auth} -s -o- https://my.public.server/dyndns/query/a/${fqdn})

if [ "${curip}" != "${homeip}" ]; then
        warnmsg="/!\\ home IP changed to ${curip} /!\\"

        echo "${warnmsg}"|mail -s "${warnmsg}" me@mydomain.net

        curl -u ${auth} \
                -X POST https://my-public.server/dyndns/update/${domain}/${name}/${curip}

Which is cron’ed to run every 5 minutes.

And here I am with my own cheap Dynamic DNS system.