diff options
Diffstat (limited to 'src/conf-mode/vyos-config-dns-forwarding.py')
-rwxr-xr-x | src/conf-mode/vyos-config-dns-forwarding.py | 197 |
1 files changed, 197 insertions, 0 deletions
diff --git a/src/conf-mode/vyos-config-dns-forwarding.py b/src/conf-mode/vyos-config-dns-forwarding.py new file mode 100755 index 000000000..7a8aed75d --- /dev/null +++ b/src/conf-mode/vyos-config-dns-forwarding.py @@ -0,0 +1,197 @@ +#!/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 sys +import os +import time +import netifaces + +from vyos.config import Config +from vyos.util import ConfigError + +config_file = r'/etc/powerdns/recursor.conf' + +# borrowed from: https://github.com/donjajo/py-world/blob/master/resolvconfReader.py, THX! +def get_resolvers(file): + resolvers = [] + try: + with open(file, 'r') as resolvconf: + for line in resolvconf.readlines(): + line = line.split('#',1)[0]; + line = line.rstrip(); + if 'nameserver' in line: + resolvers.append(line.split()[1]) + return resolvers + except IOError as error: + return error.strerror + +def get_config(): + dns = {} + conf = Config() + conf.set_level('service dns forwarding') + if not conf.exists(''): + return dns + + if conf.exists('cache-size'): + cache = conf.return_value('cache-size') + dns.setdefault('cache-size', cache) + + if conf.exists('dhcp'): + dns.setdefault('dhcp', []) + interfaces = [] + interfaces = conf.return_values('dhcp') + for interface in interfaces: + resolvers = get_resolvers("/etc/resolv.conf.dhclient-new-{0}".format(interface)) + dhcp = { + "interface": interface, + "resolvers": resolvers + } + dns['dhcp'].append(dhcp) + + if conf.exists('domain'): + dns.setdefault('domain', []) + for node in conf.list_nodes('domain'): + server = conf.return_values("domain {0} server".format(node)) + domain = { + "name": node, + "server": server + } + dns['domain'].append(domain) + + if conf.exists('ignore-hosts-file'): + dns.setdefault('ignore-hosts-file', True) + + if conf.exists('listen-on'): + interfaces = [] + interfaces = conf.return_values('listen-on') + dns.setdefault('listen-on', interfaces) + + if conf.exists('name-server'): + nameservers = [] + nameservers = conf.return_values('name-server') + dns.setdefault('name-server', nameservers) + + if conf.exists('system'): + conf.set_level('system') + nameservers = [] + nameservers = conf.return_values('name-server') + if len(nameservers) == 0: + print("DNS forwarding warning: No name-servers set under 'system name-server'\n") + else: + dns.setdefault('system-name-server', nameservers) + + return dns + +def verify(dns): + if len(dns) > 0: + if 'listen-on' not in dns.keys(): + raise ConfigError('Error: DNS forwarding requires a configured listen interface!') + + for interface in dns['listen-on']: + try: + netifaces.ifaddresses(interface)[netifaces.AF_INET] + except KeyError as e: + raise ConfigError('Error: Interface {0} has no IP address assigned'.format(interface)) + + if 'domain' in dns.keys(): + for domain in dns['domain']: + if len(domain['server']) == 0: + raise ConfigError('Error: No server configured for domain {0}'.format(domain['name'])) + + return None + +def generate(dns): + config_header = '### Autogenerated by vyos-config-dns-forwarding.py on {tm} ###\n'.format(tm=time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime())) + + fwds = [] + fwds_src = [] + + # write new configuration file + f = open(config_file, 'w') + f.write(config_header) + f.write('daemon=yes\n') + f.write('threads=1\n') + f.write('allow-from=0.0.0.0/0\n') + f.write('log-common-errors=yes\n') + + if 'listen-on' in dns.keys(): + listen4 = [] + listen6 = [] + for interface in dns['listen-on']: + addrs = netifaces.ifaddresses(interface) + for ip4 in addrs[netifaces.AF_INET]: + listen4.append(ip4['addr']) + + for ip6 in addrs[netifaces.AF_INET6]: + listen6.append(ip6['addr']) + + # build local-address string, example: + # local-address=172.16.37.240, 127.0.0.1, 172.16.254.35, 172.16.77.1, 2001:DB8::1:25, ::1 + f.write('local-address=' + ', '.join(listen4) + ', ' + ', '.join(listen6) + '\n') + + if 'cache-size' in dns.keys(): + f.write("max-cache-entries={0}\n".format(dns['cache-size'])) + + if 'dhcp' in dns.keys(): + for dhcp in dns['dhcp']: + fwds_src.append('DHCP: {0} ({1})'.format(dhcp['interface'], ', '.join(str(ns) for ns in dhcp['resolvers']))) + fwds.append(', '.join(str(ns) for ns in dhcp['resolvers'])) + + if 'domain' in dns.keys(): + zones = [] + for domain in dns['domain']: + zones.append('{0}={1}'.format(domain['name'], ';'.join(str(ns) for ns in domain['server']))) + f.write('forward-zones=' + ', '.join(zones) + '\n') + + if 'ignore-hosts-file' in dns.keys(): + f.write("export-etc-hosts=no\n") + + if 'name-server' in dns.keys(): + fwds_src.append('statically configured: {0}'.format(', '.join(str(ns) for ns in dns['name-server']))) + fwds.append(', '.join(str(ns) for ns in dns['name-server'])) + + if 'system-name-server' in dns.keys(): + fwds_src.append('system: {0}'.format(', '.join(str(ns) for ns in dns['system-name-server']))) + fwds.append(', '.join(str(ns) for ns in dns['system-name-server'])) + + if len(fwds) > 0: + f.write('\n') + f.write('# ' + '\n# '.join(fwds_src) + '\n') + f.write('forward-zones-recurse=.=' + '; '.join(fwds) + '\n') + + f.close() + return None + +def apply(dns): + if len(dns) == 0: + cmd = "sudo systemctl stop pdns-recursor" + else: + cmd = "sudo systemctl restart pdns-recursor" + + os.system(cmd) + return None + +if __name__ == '__main__': + try: + c = get_config() + verify(c) + generate(c) + apply(c) + except ConfigError as e: + print(e) + sys.exit(1) |