From c0851fa918c50b4ebc3d8bb51a6b686e1285a058 Mon Sep 17 00:00:00 2001 From: Lukas Oyen Date: Thu, 29 Jun 2017 15:30:14 +0200 Subject: [PATCH 1/3] Bug #xxx: umc-diagnostic: new check `kerberos_ddns_update.py` Original `kerberos_auth_dns_updates.py` by Julian Hupertz (with slight cleanup). --- .../diagnostic/plugins/kerberos_ddns_update.py | 86 ++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py diff --git a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py new file mode 100644 index 0000000..42c64bd --- /dev/null +++ b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py @@ -0,0 +1,86 @@ +#!/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 subprocess + +import univention.config_registry +from univention.management.console.modules.diagnostic import Critical + +from univention.lib.i18n import Translation +_ = Translation('univention-management-console-module-diagnostic').translate + +title = _('Check for Kerberos-authenticated DNS Updates') +description = _('The check for updating DNS-Records with Kerberos Authentication was successful.') + + +def run(): + config_registry = univention.config_registry.ConfigRegistry() + config_registry.load() + + pwdpath = config_registry.get("umc/module/diagnostic/umc_password") + hostname = config_registry.get("umc/module/diagnostic/umc_user") + domainname = config_registry.get("domainname") + kerberos_realm = config_registry.get("kerberos/realm") + process = subprocess.Popen(['testparm', '-sv'], stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, stderr = process.communicate() + samba_server_role = utils.get_string_between_strings(stdout, "server role = ", "\n") + + if not samba_server_role == "active directory domain controller": + return + + kinit = ['kinit', '--password-file=%s' % (pwdpath), '%s@%s' % (hostname, kerberos_realm)] + process = subprocess.Popen(kinit, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + if process.returncode: + description = _('Kinit with machine account failed') + raise Critical('\n'.join([ + description, + "Returncode of process: %s" % (process.returncode) + ])) + stdout, stderr = process.communicate() + nsupdate_process = subprocess.Popen(['nsupdate', '-g'], + stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) + nsupdate_process.stdin.write('prereq yxdomain %s\nsend\nquit\n' % (domainname)) + stdout, stderr = nsupdate_process.communicate() + if nsupdate_process.returncode != 0: + description = _('nsupdate -g failed') + raise Critical('\n'.join([ + description, + "Returncode of process: %s" % (nsupdate_process.returncode), + "stdout: %s" % (stdout), + "stderr: %s" % (stderr) + ])) + +if __name__ == '__main__': + from univention.management.console.modules.diagnostic import main + main() -- 2.7.4 From 2f319b7a252485f6473adab241ef3740fb9b4a00 Mon Sep 17 00:00:00 2001 From: Lukas Oyen Date: Thu, 29 Jun 2017 16:11:17 +0200 Subject: [PATCH 2/3] Bug #xxx: umc-diagnostic: simplify and improve `kerberos_ddns_update.py` --- .../diagnostic/plugins/kerberos_ddns_update.py | 123 +++++++++++++++------ 1 file changed, 88 insertions(+), 35 deletions(-) mode change 100644 => 100755 management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py diff --git a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py old mode 100644 new mode 100755 index 42c64bd..5c9ce5c --- a/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py +++ b/management/univention-management-console-module-diagnostic/umc/python/diagnostic/plugins/kerberos_ddns_update.py @@ -32,6 +32,7 @@ # . import subprocess +import contextlib import univention.config_registry from univention.management.console.modules.diagnostic import Critical @@ -39,47 +40,99 @@ from univention.management.console.modules.diagnostic import Critical from univention.lib.i18n import Translation _ = Translation('univention-management-console-module-diagnostic').translate -title = _('Check for Kerberos-authenticated DNS Updates') -description = _('The check for updating DNS-Records with Kerberos Authentication was successful.') +title = _('Check kerberos authenticated DNS updates') +description = _('No errors occured.') + + +class UpdateError(Exception): + pass + + +class KinitError(UpdateError): + def __init__(self, principal, keytab, password_file): + super(KinitError, self).__init__(principal, keytab, password_file) + self.principal = principal + self.keytab = keytab + self.password_file = password_file + + def __str__(self): + if self.keytab: + msg = _('`kinit` for principal {princ} with keytab {tab} failed.') + else: + msg = _('`kinit` for principal {princ} with password file {file} failed.') + return msg.format(princ=self.principal, tab=self.keytab, file=self.password_file) + + +class NSUpdateError(UpdateError): + def __init__(self, hostname, domainname): + super(NSUpdateError, self).__init__(hostname, domainname) + self.hostname = hostname + self.domainname = domainname + + def __str__(self): + msg = _('`nsupdate` existence check for domain {domain} failed.') + return msg.format(domain=self.domainname) + + +@contextlib.contextmanager +def kinit(principal, keytab=None, password_file=None): + auth = '--keytab={tab}' if keytab else '--password-file={file}' + cmd = ('kinit', auth.format(tab=keytab, file=password_file), principal) + try: + subprocess.check_call(cmd) + except subprocess.CalledProcessError: + raise KinitError(principal, keytab, password_file) + else: + yield + subprocess.call(('kdestroy',)) + + +def nsupdate(hostname, domainname): + process = subprocess.Popen(('nsupdate', '-g'), stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + cmd = 'server {host}.{domain}\nprereq yxdomain {domain}\n' + _ = process.communicate(cmd.format(host=hostname, domain=domainname)) + if process.poll() != 0: + raise NSUpdateError(hostname, domainname) + + +def check_dns_machine_principal(hostname, domainname): + with kinit('{}$'.format(hostname), password_file='/etc/machine.secret'): + nsupdate(hostname, domainname) + + +def check_dns_server_principal(hostname, domainname): + with kinit('dns-{}'.format(hostname), keytab='/var/lib/samba/private/dns.keytab'): + nsupdate(hostname, domainname) + + +def check_nsupdate(hostname, domainname, is_dc=False): + try: + check_dns_machine_principal(hostname, domainname) + except UpdateError as error: + yield error + + if is_dc: + try: + check_dns_server_principal(hostname, domainname) + except UpdateError as error: + yield error def run(): config_registry = univention.config_registry.ConfigRegistry() config_registry.load() - pwdpath = config_registry.get("umc/module/diagnostic/umc_password") - hostname = config_registry.get("umc/module/diagnostic/umc_user") - domainname = config_registry.get("domainname") - kerberos_realm = config_registry.get("kerberos/realm") - process = subprocess.Popen(['testparm', '-sv'], stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) - stdout, stderr = process.communicate() - samba_server_role = utils.get_string_between_strings(stdout, "server role = ", "\n") - - if not samba_server_role == "active directory domain controller": - return - - kinit = ['kinit', '--password-file=%s' % (pwdpath), '%s@%s' % (hostname, kerberos_realm)] - process = subprocess.Popen(kinit, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - if process.returncode: - description = _('Kinit with machine account failed') - raise Critical('\n'.join([ - description, - "Returncode of process: %s" % (process.returncode) - ])) - stdout, stderr = process.communicate() - nsupdate_process = subprocess.Popen(['nsupdate', '-g'], - stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT) - nsupdate_process.stdin.write('prereq yxdomain %s\nsend\nquit\n' % (domainname)) - stdout, stderr = nsupdate_process.communicate() - if nsupdate_process.returncode != 0: - description = _('nsupdate -g failed') - raise Critical('\n'.join([ - description, - "Returncode of process: %s" % (nsupdate_process.returncode), - "stdout: %s" % (stdout), - "stderr: %s" % (stderr) - ])) + hostname = config_registry.get('hostname') + domainname = config_registry.get('domainname') + is_dc = config_registry.get('samba4/role') == 'DC' + + problems = list(check_nsupdate(hostname, domainname, is_dc)) + if problems: + ed = [_('Errors occured while running `kinit` or `nsupdate`.')] + ed.extend(str(error) for error in problems) + raise Critical(description='\n'.join(ed)) + if __name__ == '__main__': from univention.management.console.modules.diagnostic import main -- 2.7.4 From 226b9fbcd4e0a1ee41b2f26124c35b2af80c11f2 Mon Sep 17 00:00:00 2001 From: Lukas Oyen Date: Thu, 29 Jun 2017 16:26:46 +0200 Subject: [PATCH 3/3] Bug #xxx: umc-diagnostic: new check `kerberos_ddns_update.py` (po) --- .../umc/python/diagnostic/de.po | 36 ++++++++++++++++++++-- 1 file changed, 34 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..55c753c 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-29 16:36+0200\n" "PO-Revision-Date: \n" "Last-Translator: Univention GmbH \n" "Language-Team: Univention GmbH \n" @@ -27,6 +27,14 @@ msgstr "" msgid "Adjust to suggested limits" msgstr "An vorgeschlagene Limits anpassen" +#: umc/python/diagnostic/plugins/kerberos_ddns_update.py:43 +msgid "Check kerberos authenticated DNS updates" +msgstr "Überprüfe Kerberos authentifizierte DNS Updates" + +#: umc/python/diagnostic/plugins/kerberos_ddns_update.py:132 +msgid "Errors occured while running `kinit` or `nsupdate`." +msgstr "Fehler traten auf bei der Ausführung von `kinit` oder `nsupdate`." + #: umc/python/diagnostic/plugins/gateway.py:11 msgid "Gateway is not reachable" msgstr "Gateway ist nicht erreichbar" @@ -97,6 +105,10 @@ msgstr "" msgid "Nameserver(s) are not responsive" msgstr "Nameserver sind nicht ansprechbar" +#: umc/python/diagnostic/plugins/kerberos_ddns_update.py:44 +msgid "No errors occured." +msgstr "Es traten keine Fehler auf." + #: umc/python/diagnostic/plugins/package_status.py:11 msgid "Package status corrupt" msgstr "Paketstatus korrupt" @@ -260,6 +272,26 @@ 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/kerberos_ddns_update.py:60 +#, python-brace-format +msgid "`kinit` for principal {princ} with keytab {tab} failed." +msgstr "" +"`kinit` für den Principal {princ} mit der Password Tabelle {tab} ist " +"fehlgeschlagen." + +#: umc/python/diagnostic/plugins/kerberos_ddns_update.py:62 +#, python-brace-format +msgid "`kinit` for principal {princ} with password file {file} failed." +msgstr "" +"`kinit` für den Principal {princ} mit der Password Datei {file} ist " +"fehlgeschlagen." + +#: umc/python/diagnostic/plugins/kerberos_ddns_update.py:73 +#, python-brace-format +msgid "`nsupdate` existence check for domain {domain} failed." +msgstr "" +"`nsupdate` Existenzprüfung für die Domänne {domain} ist fehlgeschlagen." + #: umc/python/diagnostic/plugins/package_status.py:28 msgid "some" msgstr "einigen" -- 2.7.4