Univention Bugzilla – Attachment 8451 Details for
Bug 43217
nameserver2=dns/forwarder1 breaks LDAP/DHCP
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
fix-ucr-dns
fix-ucr-dns (text/plain), 10.41 KB, created by
Arvid Requate
on 2017-02-22 20:35:24 CET
(
hide
)
Description:
fix-ucr-dns
Filename:
MIME Type:
Creator:
Arvid Requate
Created:
2017-02-22 20:35:24 CET
Size:
10.41 KB
patch
obsolete
>#!/usr/bin/python2.7 >""" >Configure UCS-domain and forward DNS servers. >""" ># Copyright 2016-2017 Univention GmbH ># ># http://www.univention.de/ ># ># All rights reserved. ># ># The source code of this program is made available ># under the terms of the GNU Affero General Public License version 3 ># (GNU AGPL V3) as published by the Free Software Foundation. ># ># Binary versions of this program provided by Univention to you as ># well as other copyrighted, protected or trademarked materials like ># Logos, graphics, fonts, specific documentations and configurations, ># cryptographic keys etc. are subject to a license agreement between ># you and Univention and not subject to the GNU AGPL V3. ># ># In the case you use this program under the terms of the GNU AGPL V3, ># the program is provided 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 Affero General Public License for more details. ># ># You should have received a copy of the GNU Affero General Public ># License with the Debian GNU/Linux or Univention distribution in file ># /usr/share/common-licenses/AGPL-3; if not, see ># <http://www.gnu.org/licenses/>. > >from os import environ >from sys import modules, stderr >from collections import OrderedDict >from subprocess import check_output, check_call >from optparse import OptionParser, SUPPRESS_HELP >from logging import getLogger, basicConfig, DEBUG, INFO, WARNING, ERROR > >from univention.config_registry import ConfigRegistry >from univention.config_registry.frontend import ucr_update >from univention.config_registry.interfaces import Interfaces >from DNS import DnsRequest, SocketError, TimeoutError >from ipaddr import IPAddress, Bytes > > >UCR_VARS_FWD = ['dns/forwarder%d' % (i,) for i in range(1, 4)] >UCR_VARS_DNS = ['nameserver%d' % (i,) for i in range(1, 4)] >LOCAL = '127.0.0.1' # or ::1 for IPv6 > > >def main(): > options = parse_args() > setup_logging(options) > log = getLogger(__name__) > > if options.run_tests: > run_tests() > > ucr = ConfigRegistry() > ucr.load() > > nameservers, forwarders = OrderedDict(), OrderedDict() > add_self(nameservers, ucr) > get_forwarders(forwarders, ucr) > get_nameservers(nameservers, forwarders, ucr) > action_required = validate_servers(nameservers, forwarders, ucr['domainname'], options) > if not action_required: > log.info("No action required.") > return > add_nameservers(nameservers, ucr['domainname'], options) > add_master(nameservers, ucr['ldap/master'], options) > update_ucr(ucr, nameservers, forwarders, options) > > >def parse_args(): > usage = '%prog [options]' > description = modules[__name__].__doc__ > parser = OptionParser(usage=usage, description=description) > parser.add_option( > '--verbose', '-v', > action='count', default=2, > help='Increase verbosity') > parser.add_option( > '--no-act', '-n', > action='store_true', > help='Enable dry-run mode') > parser.add_option( > '--ipv6', '-6', > action='store_const', const=('A', 'AAAA'), default=('A',), dest='rrtyp', > help='Also add IPv6 addresses') > parser.add_option( > '--no-master', '-M', > action='store_true', > help='Do not add domaincontroller_master as name-server') > parser.add_option( > '--no-nameservers', '-N', > action='store_true', > help='Do not add other name-servers') > parser.add_option( > '--no-validation', '-V', > action='store_true', > help='Do not validate DNS servers') > parser.add_option( > '--run-tests', > action='store_true', > help=SUPPRESS_HELP) > > options, args = parser.parse_args() > if args: > parser.error('No argument expected') > > return options > > >def setup_logging(options): > FORMAT = '%(asctime)-15s %(levelname)-7s %(name)-17s %(message)s' > LEVELS = [ERROR, WARNING, INFO, DEBUG] > try: > level = LEVELS[options.verbose] > except IndexError: > level = LEVELS[-1] > basicConfig(format=FORMAT, level=level, stream=stderr) > > >def add_self(nameservers, ucr): > global myself > > log = getLogger(__name__).getChild('ucr/self') > iface = Interfaces(ucr) > mynet = iface.get_default_ip_address() > myself = mynet.ip > log.info('Default IP address configured in UCR: %s', myself) > > log.debug('Checking DNS server is responding on %s', myself) > try: > r = DnsRequest(ucr['domainname'], qtype='SOA', server=['%s' % (myself,)], aa=1, rd=0).req() > log.debug('header=%r', r.header) > except (SocketError, TimeoutError) as exc: > log.error('Query failed (%s), skip putting myself as nameserver1', exc.args[0]) > else: > nameservers[myself] = None > > >def get_forwarders(forwarders, ucr): > log = getLogger(__name__).getChild('ucr/fwd') > > for var in UCR_VARS_FWD: > fwd = ucr.get(var, '').strip() > if not fwd: > continue > fwd = IPAddress(fwd) > if is_self(fwd): > log.info("Found local interface address %s configured as %s, dropping that because it's cyclic", fwd, var) > continue > else: > log.info('Found server %s configured as %s', fwd, var) > forwarders[fwd] = None > > >def get_nameservers(nameservers, forwarders, ucr): > log = getLogger(__name__).getChild('ucr/ns') > > for var in UCR_VARS_DNS: > dns = ucr.get(var, '').strip() > if not dns: > continue > dns = IPAddress(dns) > if dns in forwarders: > log.info('Dropping %s as %s, already configured as forwarder', dns, var) > continue > if is_self(dns): > log.info('Found local interface address %s configured as %s', dns, var) > if nameservers and nameservers.keys()[0] == myself: > continue > else: > log.info('Found server %s configured as %s', dns, var) > nameservers[dns] = None > > >def validate_servers(nameservers, forwarders, domain, options): > log = getLogger(__name__).getChild('val') > if options.no_validation: > log.info('Skip validation of DNS servers') > return > > action_required = False > rec = '_domaincontroller_master._tcp.%s.' % (domain.rstrip('.'),) > for server in nameservers: > log.debug('Querying %s for SRV %s', server, rec) > try: > r = DnsRequest(rec, qtype='SRV', server=['%s' % (server,)], aa=1, rd=0).req() > log.debug('header=%r', r.header) > except (SocketError, TimeoutError) as exc: > log.error('Connection check to %s (%s) failed, maybe down?!', server, exc.args[0]) > log.info('Leaving it configured as nameserver anyway') > continue > > if r.header['status'] == 'NOERROR' and r.header['aa']: > log.info('Validated UCS domain server: %s', server) > else: > log.warn('UCS master SRV record is unknown at %s, converting into forwarder', server) > action_required = True > del nameservers[server] > forwarders[server] = None > return action_required > > >def add_nameservers(nameservers, domain, options): > log = getLogger(__name__).getChild('ns') > if options.no_nameservers: > log.info('Skip adding NS') > return > > log.debug('Querying %s for additional NS records in %s', LOCAL, domain) > try: > r = DnsRequest(domain, qtype='NS', server=[LOCAL], aa=1, rd=0).req() > log.debug('header=%r', r.header) > except (SocketError, TimeoutError) as exc: > log.error('Querying %s failed (%s), skip adding NS', LOCAL, exc.args[0]) > return > > if r.header['status'] == 'NOERROR' and r.header['aa']: > names = set(rr['data'] for rr in r.answers) > log.debug('servers=%r', names) > for rr in r.additional: > log.debug('rr=%r', rr) > name = rr['name'] > if rr['typename'] in options.rrtyp and name in names: > ip = get_ip(rr) > if is_self(ip): > log.info('Skipping local interface address %s found for NS record %s', ip, name) > continue > log.info('Adding server found in NS: %s=%s', name, ip) > nameservers[ip] = None > names.remove(name) > else: > log.error('DNS lookup of NS records in %s against %s failed', domain, LOCAL) > > >def add_master(nameservers, master, options): > log = getLogger(__name__).getChild('ldap') > if options.no_master: > log.info('Skip adding master') > return > > log.debug('Querying %s for address of master %s', LOCAL, master) > try: > r = DnsRequest(master, qtype='ANY', server=[LOCAL], aa=1, rd=0).req() > log.debug('header=%r', r.header) > except (SocketError, TimeoutError) as exc: > log.error('Querying %s failed (%s), skip adding master', LOCAL, exc.args[0]) > return > > if r.header['status'] == 'NOERROR' and r.header['aa']: > for rr in r.answers: > log.debug('rr=%r', rr) > if rr['typename'] in options.rrtyp: > ip = get_ip(rr) > if is_self(ip): > log.info('Skipping local interface address %s found for ldap/master %s', ip, master) > continue > log.info('Adding master %s', ip) > nameservers[ip] = None > break > else: > log.error('DNS lookup of %s against %s failed', master, LOCAL) > > >def show_ucr_changes(names, ucr, new_ucr_settings): > log = getLogger(__name__).getChild('ucr') > for var in names: > old = ucr.get(var, None) > new = new_ucr_settings.get(var, None) > if new == old: > continue > if not new: > log.warn("unset '%s' old:%s", var, old) > elif not old: > log.warn("set %s=%s old:[Previously undefined]", var, new) > else: > log.warn("set %s=%s old:%s", var, new, old) > >def update_ucr(ucr, nameservers, forwarders, options): > log = getLogger(__name__).getChild('ucr') > new_ucr_settings = {} > > def update(names, values, typ): > log.debug('%s=%r', typ, values) > values = map(str, values) > diff = len(names) - len(values) > if diff > 0: > values += [None] * diff > elif diff < 0: > log.warn('Skipping extra %s: %r', typ, values[len(names):]) > new_ucr_settings.update(dict(zip(names, values))) > > update(UCR_VARS_FWD, forwarders, 'forwarders') > update(UCR_VARS_DNS, nameservers, 'nameservers') > > for names in (UCR_VARS_FWD, UCR_VARS_DNS): > show_ucr_changes(names, ucr, new_ucr_settings) > > # log.info('Updating %r', new_ucr_settings) > > if not options.no_act: > ucr_update(ucr, new_ucr_settings) > > log.info('Reloading BIND') > check_call(('rndc', 'reconfig')) > > >def get_ip(rr): > r""" > >>> get_ip({'typename': 'A', 'data': '127.0.0.1'}) > IPv4Address('127.0.0.1') > >>> get_ip({'typename': 'AAAA', 'data': '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01'}) > IPv6Address('::1') > """ > typ, data = rr['typename'], rr['data'] > if typ == 'A': > return IPAddress(data) > elif typ == 'AAAA': > # Work-around bug in python-pydns, which does not unpack IPv6 addresses > assert len(data) == 16 > _cb = Bytes if issubclass(Bytes, str) else lambda bytestr: bytes(bytestr, 'charmap') > return IPAddress(_cb(data)) > else: > raise TypeError(typ) > > >def is_self(addr): > """ > >>> is_self('127.0.0.1') > True > >>> is_self('::1') > True > >>> is_self('8.8.8.8') > False > """ > log = getLogger(__name__).getChild('ip') > > env = dict(environ) > env['LC_ALL'] = 'C' > cmd = ['ip', 'route', 'get', '%s' % addr] > log.debug('calling %r', cmd) > out = check_output(cmd, env=env) > return out.startswith('local ') > > >def run_tests(): > import doctest > doctest.testmod() > > >if __name__ == '__main__': > main()
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 43217
:
8309
|
8451
|
8471