From 58cb8f450b6410c40e22f8d1f66867977d09e252 Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Apr 16 2013 10:51:48 +0000 Subject: Use A/AAAA records instead of CNAME records in ipa-ca. https://fedorahosted.org/freeipa/ticket/3547 --- diff --git a/install/share/bind.zone.db.template b/install/share/bind.zone.db.template index 5ee71d6..6795bb0 100644 --- a/install/share/bind.zone.db.template +++ b/install/share/bind.zone.db.template @@ -26,4 +26,4 @@ _kpasswd._udp IN SRV 0 100 464 $HOST $OPTIONAL_NTP ; CNAME for IPA CA replicas (used for CRL, OCSP) -$IPA_CA_CNAME IN CNAME $HOST +$IPA_CA_RECORD diff --git a/install/tools/ipa-ca-install b/install/tools/ipa-ca-install index f8f7e1d..2ebce60 100755 --- a/install/tools/ipa-ca-install +++ b/install/tools/ipa-ca-install @@ -85,7 +85,7 @@ def install_dns_records(config, options): try: api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), bind_pw=config.dirman_password) - bind.add_ipa_ca_cname(config.host_name, config.domain_name) + bind.add_ipa_ca_dns_records(config.host_name, config.domain_name) finally: if api.Backend.ldap2.isconnected(): api.Backend.ldap2.disconnect() diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage index e78f9d2..a339a50 100755 --- a/install/tools/ipa-replica-manage +++ b/install/tools/ipa-replica-manage @@ -697,6 +697,7 @@ def del_master(realm, hostname, options): api.Backend.ldap2.connect(ccache=ccache) bind = bindinstance.BindInstance() bind.remove_master_dns_records(hostname, realm, realm.lower()) + bind.remove_ipa_ca_dns_records(hostname, realm.lower()) except Exception, e: print "Failed to cleanup %s DNS entries: %s" % (hostname, convert_error(e)) print "You may need to manually remove them from the tree" diff --git a/install/tools/ipa-upgradeconfig b/install/tools/ipa-upgradeconfig index f71d834..8bd7ff2 100644 --- a/install/tools/ipa-upgradeconfig +++ b/install/tools/ipa-upgradeconfig @@ -628,31 +628,35 @@ def migrate_crl_publish_dir(ca): 'request pki-ca restart') return True -def add_server_cname_records(): - root_logger.info('[Add missing server CNAME records]') +def add_ca_dns_records(): + root_logger.info('[Add missing CA DNS records]') - if not sysupgrade.get_upgrade_state('dns', 'ipa_ca_cname'): - try: - api.Backend.ldap2.connect(autobind=True) - except ipalib.errors.PublicError, e: - root_logger.error("Cannot connect to LDAP to add DNS records: %s", e) - else: - ret = api.Command['dns_is_enabled']() - if not ret['result']: - root_logger.info('DNS is not configured') - sysupgrade.set_upgrade_state('dns', 'ipa_ca_cname', True) - return - - bind = bindinstance.BindInstance() - # DNS is enabled, so let bindinstance find out if CA is enabled - # and let it add the CNAME in that case - bind.add_ipa_ca_cname(api.env.host, api.env.domain, ca_configured=None) - sysupgrade.set_upgrade_state('dns', 'ipa_ca_cname', True) - finally: - if api.Backend.ldap2.isconnected(): - api.Backend.ldap2.disconnect() - else: - root_logger.info('IPA CA CNAME already processed') + if sysupgrade.get_upgrade_state('dns', 'ipa_ca_records'): + root_logger.info('IPA CA DNS records already processed') + return + + try: + api.Backend.ldap2.connect(autobind=True) + except ipalib.errors.PublicError, e: + root_logger.error("Cannot connect to LDAP to add DNS records: %s", e) + return + + ret = api.Command['dns_is_enabled']() + if not ret['result']: + root_logger.info('DNS is not configured') + sysupgrade.set_upgrade_state('dns', 'ipa_ca_records', True) + return + + bind = bindinstance.BindInstance() + + bind.convert_ipa_ca_cnames(api.env.domain) + + # DNS is enabled, so let bindinstance find out if CA is enabled + # and let it add the record in that case + bind.add_ipa_ca_dns_records(api.env.host, api.env.domain, + ca_configured=None) + + sysupgrade.set_upgrade_state('dns', 'ipa_ca_records', True) def main(): """ @@ -746,7 +750,7 @@ def main(): cleanup_kdc(fstore) setup_firefox_extension(fstore) - add_server_cname_records() + add_ca_dns_records() changed_psearch = named_enable_psearch() changed_autoincrement = named_enable_serial_autoincrement() if changed_psearch or changed_autoincrement: diff --git a/ipaserver/install/bindinstance.py b/ipaserver/install/bindinstance.py index a528320..bc250f0 100644 --- a/ipaserver/install/bindinstance.py +++ b/ipaserver/install/bindinstance.py @@ -28,7 +28,7 @@ import ldap import service from ipaserver import ipaldap from ipaserver.install.dsinstance import realm_to_serverid -from ipaserver.install.cainstance import IPA_CA_CNAME +from ipaserver.install.cainstance import IPA_CA_RECORD from ipaserver.install.installutils import resolve_host from ipapython import sysrestore from ipapython import ipautil @@ -334,6 +334,13 @@ def del_rr(zone, name, type, rdata): except (errors.NotFound, errors.AttrValueNotFound, errors.EmptyModlist): pass +def del_fwd_rr(zone, host, ip_address): + addr = netaddr.IPAddress(ip_address) + if addr.version == 4: + del_rr(zone, host, "A", ip_address) + elif addr.version == 6: + del_rr(zone, host, "AAAA", ip_address) + def get_rr(zone, name, type): rectype = '%srecord' % unicode(type.lower()) ret = api.Command.dnsrecord_find(unicode(zone), unicode(name)) @@ -344,6 +351,9 @@ def get_rr(zone, name, type): return [] +def get_fwd_rr(zone, host): + return [x for t in ("A", "AAAA") for x in get_rr(zone, host, t)] + def zonemgr_callback(option, opt_str, value, parser): """ Properly validate and convert --zonemgr Option to IA5String @@ -500,7 +510,7 @@ class BindInstance(service.Service): if self.reverse_zone is not None: self.step("setting up reverse zone", self.__setup_reverse_zone) self.step("setting up our own record", self.__add_self) - self.step("setting up CA CNAME record", self.__add_ipa_ca_cname) + self.step("setting up CA record", self.__add_ipa_ca_record) self.step("setting up kerberos principal", self.__setup_principal) self.step("setting up named.conf", self.__setup_named_conf) @@ -545,6 +555,15 @@ class BindInstance(service.Service): else: optional_ntp = "" + addr = netaddr.IPAddress(self.ip_address) + if addr.version in (4, 6): + ipa_ca = "%s\t\t\tIN %s\t\t\t%s\n" % ( + IPA_CA_RECORD, + "A" if addr.version == 4 else "AAAA", + self.ip_address) + else: + ipa_ca = "" + boolean_var = {} for var in ('persistent_search', 'serial_autoincrement'): boolean_var[var] = "yes" if getattr(self, var, False) else "no" @@ -560,7 +579,7 @@ class BindInstance(service.Service): OPTIONAL_NTP=optional_ntp, ZONEMGR=self.zonemgr, ZONE_REFRESH=self.zone_refresh, - IPA_CA_CNAME=IPA_CA_CNAME, + IPA_CA_RECORD=ipa_ca, PERSISTENT_SEARCH=boolean_var['persistent_search'], SERIAL_AUTOINCREMENT=boolean_var['serial_autoincrement'],) @@ -587,27 +606,35 @@ class BindInstance(service.Service): def __add_self_ns(self): add_ns_rr(self.domain, api.env.host, self.dns_backup, force=True) - def __add_ipa_ca_cname(self): - if self.ca_configured is False: - root_logger.debug("CA is not configured, skip this step") + def _add_ipa_ca_dns_records(self, domain_name, fqdn, addrs, ca_configured): + if ca_configured is False: + root_logger.debug("CA is not configured") return - elif self.ca_configured is None: + elif ca_configured is None: # we do not know if CA is configured for this host and we can - # add the CA CNAME record. So we need to find out + # add the CA record. So we need to find out root_logger.debug("Check if CA is enabled for this host") - base_dn = DN(('cn', api.env.host), ('cn', 'masters'), ('cn', 'ipa'), + base_dn = DN(('cn', fqdn), ('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn) ldap_filter = '(&(objectClass=ipaConfigObject)(cn=CA))' try: api.Backend.ldap2.find_entries(filter=ldap_filter, base_dn=base_dn) except ipalib.errors.NotFound: - # CA is not configured root_logger.debug("CA is not configured") return else: - root_logger.debug("CA is configured for this host, continue") + root_logger.debug("CA is configured for this host") + + try: + for addr in addrs: + add_fwd_rr(domain_name, IPA_CA_RECORD, addr) + except errors.ValidationError: + # there is a CNAME record in ipa-ca, we can't add A/AAAA records + pass - add_rr(self.domain, IPA_CA_CNAME, "CNAME", self.host_in_rr) + def __add_ipa_ca_record(self): + self._add_ipa_ca_dns_records(self.domain, self.fqdn, [self.ip_address], + self.ca_configured) def __add_self(self): zone = self.domain @@ -720,14 +747,62 @@ class BindInstance(service.Service): self.ca_configured = ca_configured self.__add_self() - self.__add_ipa_ca_cname() + self.__add_ipa_ca_record() - def add_ipa_ca_cname(self, fqdn, domain_name, ca_configured=True): - self.host = fqdn.split(".")[0] - self.fqdn = fqdn - self.domain = domain_name - self.ca_configured = ca_configured - self.__add_ipa_ca_cname() + def add_ipa_ca_dns_records(self, fqdn, domain_name, ca_configured=True): + host, zone = fqdn.split(".", 1) + if dns_zone_exists(zone): + addrs = get_fwd_rr(zone, host) + else: + addrs = installutils.resolve_host(fqdn) + + self._add_ipa_ca_dns_records(domain_name, fqdn, addrs, ca_configured) + + def convert_ipa_ca_cnames(self, domain_name): + # get ipa-ca CNAMEs + cnames = get_rr(domain_name, IPA_CA_RECORD, "CNAME") + if not cnames: + return + + root_logger.info('Converting IPA CA CNAME records to A/AAAA records') + + # create CNAME to FQDN mapping + cname_fqdn = {} + for cname in cnames: + if cname.endswith('.'): + fqdn = cname[:-1] + else: + fqdn = '%s.%s' % (cname, domain_name) + cname_fqdn[cname] = fqdn + + # get FQDNs of all IPA masters + ldap = api.Backend.ldap2 + try: + entries, truncated = ldap.find_entries( + base_dn=DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), + api.env.basedn), + scope=ldap.SCOPE_ONELEVEL, attrs_list=['cn']) + masters = set(e[1]['cn'][0] for e in entries) + except errors.NotFound: + masters = set() + + # check if all CNAMEs point to IPA masters + for cname in cnames: + fqdn = cname_fqdn[cname] + if fqdn not in masters: + root_logger.warning( + "Cannot convert IPA CA CNAME records to A/AAAA records, " + "please convert them manually if necessary") + return + + # delete all CNAMEs + for cname in cnames: + del_rr(domain_name, IPA_CA_RECORD, "CNAME", cname) + + # add A/AAAA records + for cname in cnames: + fqdn = cname_fqdn[cname] + self.add_ipa_ca_dns_records(fqdn, domain_name, None) def remove_master_dns_records(self, fqdn, realm_name, domain_name): host = fqdn.split(".")[0] @@ -746,16 +821,15 @@ class BindInstance(service.Service): ("_kpasswd._tcp", "SRV", "0 100 464 %s" % self.host_in_rr), ("_kpasswd._udp", "SRV", "0 100 464 %s" % self.host_in_rr), ("_ntp._udp", "SRV", "0 100 123 %s" % self.host_in_rr), - (IPA_CA_CNAME, "CNAME", self.host_in_rr), ("@", "NS", fqdn+"."), ) for (record, type, rdata) in resource_records: del_rr(zone, record, type, rdata) - areclist = [("A", x) for x in get_rr(zone, host, "A")] + [("AAAA", x) for x in get_rr(zone, host, "AAAA")] - for (type, rdata) in areclist: - del_rr(zone, host, type, rdata) + areclist = get_fwd_rr(zone, host) + for rdata in areclist: + del_fwd_rr(zone, host, rdata) rzone = find_reverse_zone(rdata) if rzone is not None: @@ -764,6 +838,16 @@ class BindInstance(service.Service): # remove also master NS record from the reverse zone del_rr(rzone, "@", "NS", fqdn+".") + def remove_ipa_ca_dns_records(self, fqdn, domain_name): + host, zone = fqdn.split(".", 1) + if dns_zone_exists(zone): + addrs = get_fwd_rr(zone, host) + else: + addrs = installutils.resolve_host(fqdn) + + for addr in addrs: + del_fwd_rr(domain_name, IPA_CA_RECORD, addr) + def check_global_configuration(self): """ Check global DNS configuration in LDAP server and inform user when it diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 0d85691..01e784f 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -71,7 +71,7 @@ PKI_DS_USER = dogtag.install_constants.DS_USER # When IPA is installed with DNS support, this CNAME should hold all IPA # replicas with CA configured -IPA_CA_CNAME = "ipa-ca" +IPA_CA_RECORD = "ipa-ca" # We need to reset the template because the CA uses the regular boot # information @@ -1275,7 +1275,7 @@ class CAInstance(service.Service): changed = False # OCSP extension - ocsp_url = 'http://%s.%s/ca/ocsp' % (IPA_CA_CNAME, ipautil.format_netloc(domain)) + ocsp_url = 'http://%s.%s/ca/ocsp' % (IPA_CA_RECORD, ipautil.format_netloc(domain)) ocsp_location_0 = installutils.get_directive( self.dogtag_constants.IPA_SERVICE_PROFILE, @@ -1302,7 +1302,7 @@ class CAInstance(service.Service): # CRL extension - crl_url = 'http://%s.%s/ipa/crl/MasterCRL.bin'% (IPA_CA_CNAME, ipautil.format_netloc(domain)) + crl_url = 'http://%s.%s/ipa/crl/MasterCRL.bin'% (IPA_CA_RECORD, ipautil.format_netloc(domain)) crl_point_0 = installutils.get_directive( self.dogtag_constants.IPA_SERVICE_PROFILE,