From ee0df8fc50ea9ee1f52c060cf125688a6aa69024 Mon Sep 17 00:00:00 2001 From: Lukas Oyen Date: Thu, 22 Jun 2017 13:56:01 +0200 Subject: [PATCH 1/2] Bug #40393: umc-diagnostic: new check check_nameserver.py --- .../python/diagnostic/plugins/check_nameservers.py | 238 +++++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100755 management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/check_nameservers.py diff --git a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/check_nameservers.py b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/check_nameservers.py new file mode 100755 index 0000000..24109cb --- /dev/null +++ b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/check_nameservers.py @@ -0,0 +1,238 @@ +#!/usr/bin/python2.7 +# coding: utf-8 +# +# Univention Management Console module: +# System Diagnosis UMC module +# +# Copyright 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 +# . + +import socket +import ldap.filter +import itertools as it + +import univention.admin.uldap +import univention.admin.modules as udm_modules +import univention.admin.objects as udm_objects + +import univention.config_registry +from univention.management.console.modules.diagnostic import Warning + +from univention.lib.i18n import Translation +_ = Translation('univention-management-console-module-diagnostic').translate + +title = _('Check nameserver entries on DNS zones') +description = _('All nameserver entries are ok.') +links = [{ + 'name': 'sdb', + 'href': _('http://sdb.univention.de/1273'), + 'label': _('Univention Support Database - Bind: zone transfer failed') +}] + + +class RecordNotFound(Exception): + pass + + +class ZoneError(Exception): + def __init__(self, nameserver): + self.nameserver = nameserver + + @property + def zone(self): + return self.nameserver.zone + + +class NoHostRecord(ZoneError): + def __str__(self): + msg = _('Found no host record (A/AAAA record) for nameserver {ns}.') + return msg.format(ns=self.nameserver.nameserver()) + + +class CnameAsNameServer(ZoneError): + def __str__(self): + msg = _('Found illegal alias record (CNAME record) for nameserver {ns}.') + return msg.format(ns=self.nameserver.nameserver()) + + +class Zone(object): + def __init__(self, udm_zone, domainname): + self.udm_zone = udm_zone + self.domainname = domainname + + @property + def kind(self): + return self.udm_zone.module + + @property + def zone(self): + if self.kind == 'dns/forward_zone': + return self.udm_zone.get('zone') + return self.udm_zone.get('subnet') + + def base(self): + if self.kind == 'dns/forward_zone': + return self.zone + return '{}.in-addr.arpa'.format(self.zone) + + def nameserver(self): + for nameserver in self.udm_zone.get('nameserver'): + yield NameServer(self, nameserver) + + def umc_link(self): + text = 'udm:dns/dns' + link = { + 'module': 'udm', + 'flavor': 'dns/dns', + 'props': { + 'openObject': { + 'objectDN': self.udm_zone.dn, + 'objectType': self.kind, + } + } + } + return (text, link) + + +class NameServer(object): + def __init__(self, zone, nameserver): + self.zone = zone + self._nameserver = nameserver + + def is_qualified(self): + return self._nameserver.endswith('.') + + def nameserver(self): + return self._nameserver.rstrip('.') + + def fqdn(self): + if self.is_qualified(): + return self.nameserver() + return '{}.{}'.format(self.nameserver(), self.zone.base()) + + def is_in_zone(self): + return not self.is_qualified() or \ + self.nameserver().endswith(self.zone.domainname) + + def _generate_splits(self, fqdn): + zn = fqdn + while True: + (rdn, zn) = zn.split('.', 1) + if rdn and zn: + yield (rdn, zn) + if '.' not in zn or zn == self.zone.domainname: + break + + def build_filter(self): + template = '(&(relativeDomainName=%s)(zoneName=%s))' + expressions = (ldap.filter.filter_format(template, (rdn, zn)) + for (rdn, zn) in self._generate_splits(self.fqdn())) + return '(|{})'.format(''.join(expressions)) + + +class UDM(object): + def __init__(self): + univention.admin.modules.update() + (self.ldap_connection, self.position) = univention.admin.uldap.getMachineConnection() + self.configRegistry = univention.config_registry.ConfigRegistry() + self.configRegistry.load() + + def lookup(self, module_name, filter_expression=''): + module = udm_modules.get(module_name) + for instance in module.lookup(None, self.ldap_connection, filter_expression): + instance.open() + yield instance + + def find(self, nameserver): + filter_expression = nameserver.build_filter() + for (dn, attr) in self.ldap_connection.search(filter_expression): + if dn: + for module in udm_modules.identify(dn, attr): + record = udm_objects.get(module, None, + self.ldap_connection, self.position, dn, attr=attr, + attributes=attr) + record.open() + return record + raise RecordNotFound() + + def all_zones(self): + domainname = self.configRegistry.get('domainname') + for zone in self.lookup('dns/forward_zone'): + yield Zone(zone, domainname) + for zone in self.lookup('dns/reverse_zone'): + yield Zone(zone, domainname) + + def check_zone(self, zone): + for nameserver in zone.nameserver(): + try: + record = self.find(nameserver) + except RecordNotFound: + if not nameserver.is_in_zone(): + try: + socket.getaddrinfo(nameserver.fqdn(), None) + except socket.gaierror: + yield NoHostRecord(nameserver) + else: + yield NoHostRecord(nameserver) + + else: + if record.module == 'dns/alias': + yield CnameAsNameServer(nameserver) + elif record.module != 'dns/host_record': + yield NoHostRecord(nameserver) + + +def find_all_zone_problems(): + udm = UDM() + for zone in udm.all_zones(): + for error in udm.check_zone(zone): + yield error + + +def run(): + ed = [_('Found errors in the nameserver entries of the following zones.') + ' ' + + _('Please refer to {sdb} for further information.')] + modules = list() + tmpl_forward = _('In forward zone {name} (see {{{link}}}):') + tmpl_reverse = _('In reverse zone {name} (see {{{link}}}):') + for (zone, group) in it.groupby(find_all_zone_problems(), lambda error: error.zone): + (text, link) = zone.umc_link() + ed.append('') + if zone.kind == 'dns/forward_zone': + ed.append(tmpl_forward.format(kind=zone.kind, name=zone.zone, link=text)) + elif zone.kind == 'dns/reverse_zone': + ed.append(tmpl_reverse.format(kind=zone.kind, name=zone.zone, link=text)) + ed.extend(str(error) for error in group) + modules.append(link) + + if modules: + raise Warning(description='\n'.join(ed), umc_modules=modules) + + +if __name__ == '__main__': + from univention.management.console.modules.diagnostic import main + main() -- 2.7.4 From 4c91f285175a6b29d1fc363b32c16a4c1fa0dc71 Mon Sep 17 00:00:00 2001 From: Lukas Oyen Date: Thu, 22 Jun 2017 16:30:47 +0200 Subject: [PATCH 2/2] Bug #40393: umc-diagnostic: new check check_nameserver.py (po) --- .../umc/python/diagnostic/de.po | 50 +++++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/de.po b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/de.po index affad86..7c69e6b 100644 --- a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/de.po +++ b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/de.po @@ -2,8 +2,8 @@ msgid "" msgstr "" "Project-Id-Version: univention-management-console-module-diagnostic\n" -"Report-Msgid-Bugs-To: packages@univention.de\n" -"POT-Creation-Date: 2016-01-14 12:19+0100\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-06-22 16:36+0200\n" "PO-Revision-Date: \n" "Last-Translator: Univention GmbH \n" "Language-Team: Univention GmbH \n" @@ -27,6 +27,29 @@ msgstr "" msgid "Adjust to suggested limits" msgstr "An vorgeschlagene Limits anpassen" +#: umc/python/diagnostic/plugins/check_nameservers.py:49 +msgid "All nameserver entries are ok." +msgstr "Alle Nameserver Einträge sind in Ordnung." + +#: umc/python/diagnostic/plugins/check_nameservers.py:48 +msgid "Check nameserver entries on DNS zones" +msgstr "Überprüfe die Nameserver Einträge der DNS Zonen" + +#: umc/python/diagnostic/plugins/check_nameservers.py:217 +msgid "Found errors in the nameserver entries of the following zones." +msgstr "" +"Es wurden Fehler in den Nameserver Einträgen der folgenden Zonen gefunden." + +#: umc/python/diagnostic/plugins/check_nameservers.py:78 +#, python-brace-format +msgid "Found illegal alias record (CNAME record) for nameserver {ns}." +msgstr "Ungültiger Alias-Record (CNAME record) für Namenserver {ns} gefunden." + +#: umc/python/diagnostic/plugins/check_nameservers.py:72 +#, python-brace-format +msgid "Found no host record (A/AAAA record) for nameserver {ns}." +msgstr "Kein Host-Record (A/AAAA record) für Namenserver {ns} gefunden." + #: umc/python/diagnostic/plugins/gateway.py:11 msgid "Gateway is not reachable" msgstr "Gateway ist nicht erreichbar" @@ -49,6 +72,16 @@ msgid "If these settings are correct the problem relies in the gateway itself:" msgstr "" "Wenn diese Einstellungen richtig sind liegt das Problem im Gateway selbst:" +#: umc/python/diagnostic/plugins/check_nameservers.py:220 +#, python-brace-format +msgid "In forward zone {name} (see {{{link}}}):" +msgstr "In der Forward-Zone {name} (siehe {{{link}}}):" + +#: umc/python/diagnostic/plugins/check_nameservers.py:221 +#, python-brace-format +msgid "In reverse zone {name} (see {{{link}}}):" +msgstr "In der Reverse-Zone {name} (siehe {{{link}}}):" + #: umc/python/diagnostic/plugins/security_limits.py:19 #, python-brace-format msgid "" @@ -116,6 +149,11 @@ msgstr "" "Bitte sicherstellen, dass die DNS-Einstellungen in {setup:network} korrekt " "konfiguriert sind." +#: umc/python/diagnostic/plugins/check_nameservers.py:218 +#, python-brace-format +msgid "Please refer to {sdb} for further information." +msgstr "Siehe {sdb} für weitere Informationen." + #: umc/python/diagnostic/plugins/ssh_connection.py:52 #, python-format msgid "" @@ -260,6 +298,14 @@ msgstr "" "dass Authentifikations-Zugangsdaten (falls existierend) korrekt sind und die " "ACL's des Proxy-Servers nicht verbieten, Anfragen an %s zu stellen." +#: umc/python/diagnostic/plugins/check_nameservers.py:53 +msgid "Univention Support Database - Bind: zone transfer failed" +msgstr "" + +#: umc/python/diagnostic/plugins/check_nameservers.py:52 +msgid "http://sdb.univention.de/1273" +msgstr "" + #: umc/python/diagnostic/plugins/package_status.py:28 msgid "some" msgstr "einigen" -- 2.7.4