Page MenuHomeVyOS Platform

dynamic_dns.py
No OneTemporary

Size
8 KB
Referenced Files
None
Subscribers
None

dynamic_dns.py

#!/usr/bin/env python3
#
# Copyright (C) 2018 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
#
import os
import sys
import jinja2
from vyos.config import Config
from vyos import ConfigError
config_file = r'/etc/ddclient.conf'
cache_file = r'/var/cache/ddclient/ddclient.cache'
config_tmpl = """
### Autogenerated by dynamic_dns.py ###
daemon=1m
syslog=yes
ssl=yes
pid=/var/run/ddclient/ddclient.pid
cache=/var/cache/ddclient/ddclient.cache
{% for interface in interfaces -%}
#
# ddclient configuration for interface "{{ interface.interface }}":
#
{% if interface.web_url -%}
use=web, web='{{ interface.web_url}}' {%- if interface.web_skip %}, web-skip='{{ interface.web_skip }}'{% endif %}
{% else -%}
use=if, if={{ interface.interface }}
{% endif -%}
{% for rfc in interface.rfc2136 -%}
{% for record in rfc.record %}
# RFC2136 dynamic DNS configuration for {{ record }}.{{ rfc.zone }}
server={{ rfc.server }}
protocol=nsupdate
password={{ rfc.keyfile }}
ttl={{ rfc.ttl }}
zone={{ rfc.zone }}
{{ record }}
{% endfor -%}
{% endfor -%}
{% for srv in interface.service %}
{% for host in srv.host %}
# DynDNS provider configuration for {{ host }}
protocol={{ srv.protocol }}
max-interval=28d
login={{ srv.login }}
password='{{ srv.password }}'
{% if srv.server -%}
server={{ srv.server }}
{% endif -%}
{{ host }}
{% endfor %}
{% endfor %}
{% endfor %}
"""
# Mapping of service name to service protocol
default_service_protocol = {
'afraid': 'freedns',
'changeip': 'changeip',
'cloudflare': 'cloudflare',
'dnspark': 'dnspark',
'dslreports': 'dslreports1',
'dyndns': 'dyndns2',
'easydns': 'easydns',
'namecheap': 'namecheap',
'noip': 'noip',
'sitelutions': 'sitelutions',
'zoneedit': 'zoneedit1'
}
default_config_data = {
'interfaces': [],
}
def get_config():
dyndns = default_config_data
conf = Config()
if not conf.exists('service dns dynamic'):
return None
else:
conf.set_level('service dns dynamic')
for interface in conf.list_nodes('interface'):
node = {
'interface': interface,
'rfc2136': [],
'service': [],
'web_skip': '',
'web_url': ''
}
# set config level to e.g. "service dns dynamic interface eth0"
conf.set_level('service dns dynamic interface {0}'.format(interface))
# Handle RFC2136 - Dynamic Updates in the Domain Name System
for rfc2136 in conf.list_nodes('rfc2136'):
rfc = {
'name': rfc2136,
'keyfile': '',
'record': [],
'server': '',
'ttl': '600',
'zone': ''
}
if conf.exists('rfc2136 {0} key'.format(rfc2136)):
rfc['keyfile'] = conf.return_value('rfc2136 {0} key'.format(rfc2136))
if conf.exists('rfc2136 {0} record'.format(rfc2136)):
rfc['record'] = conf.return_values('rfc2136 {0} record'.format(rfc2136))
if conf.exists('rfc2136 {0} server'.format(rfc2136)):
rfc['server'] = conf.return_value('rfc2136 {0} server'.format(rfc2136))
if conf.exists('rfc2136 {0} ttl'.format(rfc2136)):
rfc['ttl'] = conf.return_value('rfc2136 {0} ttl'.format(rfc2136))
if conf.exists('rfc2136 {0} zone'.format(rfc2136)):
rfc['zone'] = conf.return_value('rfc2136 {0} zone'.format(rfc2136))
node['rfc2136'].append(rfc)
# Handle DynDNS service providers
for service in conf.list_nodes('service'):
srv = {
'provider': service,
'host': [],
'login': '',
'password': '',
'protocol': '',
'server': '',
'custom' : False
}
# preload protocol from default service mapping
if service in default_service_protocol.keys():
srv['protocol'] = default_service_protocol[service]
else:
srv['custom'] = True
if conf.exists('service {0} login'.format(service)):
srv['login'] = conf.return_value('service {0} login'.format(service))
if conf.exists('service {0} host-name'.format(service)):
srv['host'] = conf.return_values('service {0} host-name'.format(service))
if conf.exists('service {0} protocol'.format(service)):
srv['protocol'] = conf.return_value('service {0} protocol'.format(service))
if conf.exists('service {0} password'.format(service)):
srv['password'] = conf.return_value('service {0} password'.format(service))
if conf.exists('service {0} server'.format(service)):
srv['server'] = conf.return_value('service {0} server'.format(service))
node['service'].append(srv)
# Additional settings in CLI
if conf.exists('use-web skip'):
node['web_skip'] = conf.return_value('use-web skip')
if conf.exists('use-web url'):
node['web_url'] = conf.return_value('use-web url')
dyndns['interfaces'].append(node)
return dyndns
def verify(dyndns):
# bail out early - looks like removal from running config
if dyndns is None:
return None
# A 'node' corresponds to an interface
for node in dyndns['interfaces']:
# RFC2136 - configuration validation
for rfc2136 in node['rfc2136']:
if not rfc2136['record']:
raise ConfigError('Set key for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
if not rfc2136['zone']:
raise ConfigError('Set zone for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
if not rfc2136['keyfile']:
raise ConfigError('Set keyfile for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
else:
if not os.path.isfile(rfc2136['keyfile']):
raise ConfigError('Keyfile for service "{0}" to send DDNS updates for interface "{1}" does not exist'.format(rfc2136['name'], node['interface']))
if not rfc2136['server']:
raise ConfigError('Set server for service "{0}" to send DDNS updates for interface "{1}"'.format(rfc2136['name'], node['interface']))
# DynDNS service provider - configuration validation
for service in node['service']:
if not service['host']:
raise ConfigError('Set host-name for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
if not service['login']:
raise ConfigError('Set login for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
if not service['password']:
raise ConfigError('Set password for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
if service['custom'] is True:
if not service['protocol']:
raise ConfigError('Set protocol for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
if not service['server']:
raise ConfigError('Set server for service "{0}" to send DDNS updates for interface "{1}"'.format(service['provider'], node['interface']))
return None
def generate(dyndns):
# bail out early - looks like removal from running config
if dyndns is None:
return None
tmpl = jinja2.Template(config_tmpl)
config_text = tmpl.render(dyndns)
with open(config_file, 'w') as f:
f.write(config_text)
return None
def apply(dyndns):
if os.path.exists(cache_file):
os.unlink(cache_file)
if dyndns is None:
os.system('/etc/init.d/ddclient stop')
else:
os.system('/etc/init.d/ddclient restart')
return None
if __name__ == '__main__':
try:
c = get_config()
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
sys.exit(1)

File Metadata

Mime Type
text/x-script.python
Expires
Mon, Dec 15, 9:09 PM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3080978
Default Alt Text
dynamic_dns.py (8 KB)

Event Timeline