From dd7fdaa77d00042d44bec23291f0d0be36bead5e Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Tue, 10 Dec 2019 00:39:43 +0200 Subject: [PATCH] DNS install check: allow overlapping zone to be from the master itself When re-running `ipa-server-install --setup-dns` on already installed server, we do not get to the check of being already installed because DNS zone overlap forces us to fail earlier. Change exception returned for this case from check_zone_overlap() to return structured information that allows to understand whether we are finding a conflict with ourselves. Use the returned information to only fail DNS check at this point if DNS zone overlap is generated by a different name server than ourselves. Reviewed-By: Christian Heimes --- ipapython/dnsutil.py | 11 +++++++---- ipaserver/install/bindinstance.py | 2 +- ipaserver/install/dns.py | 8 +++++--- ipaserver/install/server/__init__.py | 13 ++++++++----- ipaserver/plugins/dns.py | 4 ++-- 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py index e51dabd37..aec1ec231 100644 --- a/ipapython/dnsutil.py +++ b/ipapython/dnsutil.py @@ -39,6 +39,12 @@ if six.PY3: logger = logging.getLogger(__name__) +class DNSZoneAlreadyExists(dns.exception.DNSException): + supp_kwargs = {'zone', 'ns'} + fmt = (u"DNS zone {zone} already exists in DNS " + "and is handled by server(s): {ns}") + + @six.python_2_unicode_compatible class DNSName(dns.name.Name): labels = None # make pylint happy @@ -374,10 +380,7 @@ def check_zone_overlap(zone, raise_on_error=True): zone, e) ns = [] - msg = u"DNS zone {0} already exists in DNS".format(zone) - if ns: - msg += u" and is handled by server(s): {0}".format(', '.join(ns)) - raise ValueError(msg) + raise DNSZoneAlreadyExists(zone=zone.to_text(), ns=ns) def _mix_weight(records): diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index 80be9ff93..fe04f763e 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -327,7 +327,7 @@ def read_reverse_zone(default, ip_address, allow_zone_overlap=False): if not allow_zone_overlap: try: dnsutil.check_zone_overlap(zone, raise_on_error=False) - except ValueError as e: + except dnsutil.DNSZoneAlreadyExists as e: logger.error("Reverse zone %s will not be used: %s", zone, e) continue diff --git a/ipaserver/install/dns.py b/ipaserver/install/dns.py index 40688dc27..36ba6f899 100644 --- a/ipaserver/install/dns.py +++ b/ipaserver/install/dns.py @@ -130,13 +130,15 @@ def install_check(standalone, api, replica, options, hostname): domain = dnsutil.DNSName(util.normalize_zone(api.env.domain)) try: dnsutil.check_zone_overlap(domain, raise_on_error=False) - except ValueError as e: + except dnsutil.DNSZoneAlreadyExists as e: if options.force or options.allow_zone_overlap: logger.warning("%s Please make sure that the domain is " "properly delegated to this IPA server.", e) - else: - raise e + + hst = dnsutil.DNSName(hostname).make_absolute().to_text() + if hst not in e.kwargs['ns']: + raise ValueError(str(e)) for reverse_zone in options.reverse_zones: try: diff --git a/ipaserver/install/server/__init__.py b/ipaserver/install/server/__init__.py index d9adf76f3..21396e1ac 100644 --- a/ipaserver/install/server/__init__.py +++ b/ipaserver/install/server/__init__.py @@ -10,6 +10,7 @@ import random from ipaclient.install import client from ipalib import constants +from ipalib.util import validate_domain_name from ipalib.install import service from ipalib.install.service import (enroll_only, installs_master, @@ -17,7 +18,6 @@ from ipalib.install.service import (enroll_only, master_install_only, prepare_only, replica_install_only) -from ipapython.dnsutil import check_zone_overlap from ipapython.install import typing from ipapython.install.core import group, knob, extend_knob from ipapython.install.common import step @@ -524,10 +524,13 @@ class ServerMasterInstall(ServerMasterInstallInterface): @domain_name.validator def domain_name(self, value): - if (self.setup_dns and - not self.allow_zone_overlap): - print("Checking DNS domain %s, please wait ..." % value) - check_zone_overlap(value, False) + # There might be an overlap but at this point we don't have + # complete installer object to verify that DNS is hosted + # by the same machine (i.e. we are already installed). + # Later, DNS.install_check will do its zone overlap check + # and will make sure to fail if overlap does really exist. + # At this point we only verify that value is a valid DNS syntax. + validate_domain_name(value) dm_password = extend_knob( ServerMasterInstallInterface.dm_password, diff --git a/ipaserver/plugins/dns.py b/ipaserver/plugins/dns.py index c3c096971..014f208b5 100644 --- a/ipaserver/plugins/dns.py +++ b/ipaserver/plugins/dns.py @@ -79,7 +79,7 @@ from ipalib.util import (normalize_zonemgr, from ipaplatform import services from ipapython.dn import DN from ipapython.ipautil import CheckedIPAddress -from ipapython.dnsutil import check_zone_overlap +from ipapython.dnsutil import check_zone_overlap, DNSZoneAlreadyExists from ipapython.dnsutil import DNSName from ipapython.dnsutil import related_to_auto_empty_zone from ipaserver.dns_data_management import ( @@ -2153,7 +2153,7 @@ class DNSZoneBase_add(LDAPCreate): if not options['skip_overlap_check']: try: check_zone_overlap(keys[-1], raise_on_error=False) - except ValueError as e: + except DNSZoneAlreadyExists as e: raise errors.InvocationError(str(e)) return dn