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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
$ cat ddns.py
from flask import Flask

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

app = Flask(__name__)

@app.route('/query/<t>/<fqdn>')
def query(t, fqdn):
resolver = dns.resolver.Resolver(configure=False)
resolver.nameservers = ['127.0.0.1']
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, '127.0.0.1', timeout=10)

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

if __name__ == "__main__":
app.run()

This code is served by gunicorn:

$ gunicorn ddns:app 

Behind an nginx reverse proxy:

1
2
3
4
5
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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh

PATH=${PATH}:/sbin:/usr/sbin:/bin:/usr/bin:/usr/pkg/bin

name="mybox"
domain="mydomain"
fqdn="${name}.${domain}"
auth="user:mymagicpassword"

# 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}
fi

Which is cron’ed to run every 5 minutes.

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