#!/usr/bin/python # # dyndns.py # # a simple ssh based dyndns updater for bind zone files # # Copyright (C) 2013 Christian Pointner # # This file is part of dyndns.py. # # dyndns.py is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # any later version. # # dyndns.py is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with dyndns.py. If not, see . # from easyzone import easyzone import subprocess import sys import os import re # domain = '{{ dyndns.domain }}.' zonefile = '{{ dyndns.zone_file }}' rndc = '/usr/sbin/rndc' ### # add this to the authorized key file of a system user # the user needs to be a member of the bind group and must # have write permissions to the zone file # no-agent-forwarding,no-port-forwarding,no-pty,no-X11-forwarding,no-user-rc,command="/usr/local/bin/dyndns.py " ssh-rsa ... ### if ('SSH_CLIENT' in os.environ) and ('SSH_ORIGINAL_COMMAND' in os.environ): sshclient = os.environ['SSH_CLIENT'] sshcmd = os.environ['SSH_ORIGINAL_COMMAND'] else: sys.stdout.write("Error: not an SSH connection\n") sys.exit(1) name = sshcmd.split()[0] if name not in set(sys.argv[1:]): sys.stdout.write("Error: you are not allowed to update '%s'\n" % name) sys.exit(1) ip = sshclient.split()[0] if not re.match(r'^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', ip): sys.stdout.write("Error: not a valid IPv4 address\n") sys.exit(1) name = name + '.' + domain zone = easyzone.zone_from_file(domain, zonefile) zone.add_name(name) records = zone.names[name].records('A', create=True, ttl=60) for item in records.get_items(): if item == ip: sys.stdout.write("Ok: nothing changed\n") sys.exit(0) records.delete(item) records.add(ip) zone.save(autoserial=True) cmd = [rndc, 'reload', domain] p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = '' for line in p.stdout.readlines(): output = output + line r = p.wait() if r != 0: sys.stdout.write("Error: rndc returned %d\n\n%s" % (r, output)) else: sys.stdout.write("Ok: " + output)