Page MenuHomeVyOS Platform

dns_forwarding.py
No OneTemporary

Size
5 KB
Referenced Files
None
Subscribers
None

dns_forwarding.py

#!/usr/bin/env python3
#
# Copyright (C) 2018-2020 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 argparse
from sys import exit
from copy import deepcopy
from vyos.config import Config
from vyos.hostsd_client import Client as hostsd_client
from vyos.util import wait_for_commit_lock
from vyos import ConfigError
from vyos.util import call
from vyos.template import render
from vyos import airbag
airbag.enable()
parser = argparse.ArgumentParser()
parser.add_argument("--dhclient", action="store_true",
help="Started from dhclient-script")
config_file = r'/run/powerdns/recursor.conf'
default_config_data = {
'allow_from': [],
'cache_size': 10000,
'export_hosts_file': 'yes',
'listen_on': [],
'name_servers': [],
'negative_ttl': 3600,
'domains': [],
'dnssec': 'process-no-validate'
}
def get_config(arguments):
dns = deepcopy(default_config_data)
conf = Config()
base = ['service', 'dns', 'forwarding']
if arguments.dhclient:
conf.exists = conf.exists_effective
conf.return_value = conf.return_effective_value
conf.return_values = conf.return_effective_values
if not conf.exists(base):
return None
conf.set_level(base)
if conf.exists(['allow-from']):
dns['allow_from'] = conf.return_values(['allow-from'])
if conf.exists(['cache-size']):
cache_size = conf.return_value(['cache-size'])
dns['cache_size'] = cache_size
if conf.exists('negative-ttl'):
negative_ttl = conf.return_value(['negative-ttl'])
dns['negative_ttl'] = negative_ttl
if conf.exists(['domain']):
for node in conf.list_nodes(['domain']):
servers = conf.return_values(['domain', node, 'server'])
domain = {
"name": node,
"servers": bracketize_ipv6_addrs(servers)
}
dns['domains'].append(domain)
if conf.exists(['ignore-hosts-file']):
dns['export_hosts_file'] = "no"
if conf.exists(['name-server']):
name_servers = conf.return_values(['name-server'])
dns['name_servers'] = dns['name_servers'] + name_servers
if conf.exists(['system']):
conf.set_level(['system'])
system_name_servers = []
system_name_servers = conf.return_values(['name-server'])
if not system_name_servers:
print("DNS forwarding warning: No name-servers set under 'system name-server'\n")
else:
dns['name_servers'] = dns['name_servers'] + system_name_servers
conf.set_level(base)
dns['name_servers'] = bracketize_ipv6_addrs(dns['name_servers'])
if conf.exists(['listen-address']):
dns['listen_on'] = conf.return_values(['listen-address'])
if conf.exists(['dnssec']):
dns['dnssec'] = conf.return_value(['dnssec'])
# Add name servers received from DHCP
if conf.exists(['dhcp']):
interfaces = []
interfaces = conf.return_values(['dhcp'])
hc = hostsd_client()
for interface in interfaces:
dhcp_resolvers = hc.get_name_servers(f'dhcp-{interface}')
dhcpv6_resolvers = hc.get_name_servers(f'dhcpv6-{interface}')
if dhcp_resolvers:
dns['name_servers'] = dns['name_servers'] + dhcp_resolvers
if dhcpv6_resolvers:
dns['name_servers'] = dns['name_servers'] + dhcpv6_resolvers
return dns
def bracketize_ipv6_addrs(addrs):
"""Wraps each IPv6 addr in addrs in [], leaving IPv4 addrs untouched."""
return ['[{0}]'.format(a) if a.count(':') > 1 else a for a in addrs]
def verify(dns):
# bail out early - looks like removal from running config
if dns is None:
return None
if not dns['listen_on']:
raise ConfigError(
"Error: DNS forwarding requires either a listen-address (preferred) or a listen-on option")
if not dns['allow_from']:
raise ConfigError(
"Error: DNS forwarding requires an allow-from network")
if dns['domains']:
for domain in dns['domains']:
if not domain['servers']:
raise ConfigError(
'Error: No server configured for domain {0}'.format(domain['name']))
return None
def generate(dns):
# bail out early - looks like removal from running config
if dns is None:
return None
render(config_file, 'dns-forwarding/recursor.conf.tmpl', dns, trim_blocks=True, user='pdns', group='pdns')
return None
def apply(dns):
if dns is None:
# DNS forwarding is removed in the commit
call("systemctl stop pdns-recursor.service")
if os.path.isfile(config_file):
os.unlink(config_file)
else:
call("systemctl restart pdns-recursor.service")
if __name__ == '__main__':
args = parser.parse_args()
if args.dhclient:
# There's a big chance it was triggered by a commit still in progress
# so we need to wait until the new values are in the running config
wait_for_commit_lock()
try:
c = get_config(args)
verify(c)
generate(c)
apply(c)
except ConfigError as e:
print(e)
exit(1)

File Metadata

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

Event Timeline