Lookup ipa-ca record with NSS

DNS data management now uses NSS's getaddrinfo() instead of direct DNS
queries to resolve the ipa-ca record. This fixes missing ipa-ca records
when the current hostname is not resolvable in DNS but has correct
records in /etc/hosts.

Reduce timeout to 15 seconds and tighten timeout loop.

The changeset can speed up installation by almost 60 seconds.
ipa-server-install without built-in DNS calls into DNS data management
twice with a timeout of 30 seconds for each call.

Fixes: https://pagure.io/freeipa/issue/8529
Related: https://pagure.io/freeipa/issue/8521
Related: https://pagure.io/freeipa/issue/8501
Signed-off-by: Christian Heimes <cheimes@redhat.com>
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
This commit is contained in:
Christian Heimes
2020-10-06 13:12:41 +02:00
parent 5d95794edf
commit 731c5b2110
2 changed files with 48 additions and 10 deletions

View File

@@ -15,13 +15,13 @@ from dns import (
rdatatype, rdatatype,
zone, zone,
) )
from dns.exception import DNSException
from time import sleep, time from time import sleep, time
from ipalib import errors from ipalib import errors
from ipalib.dns import record_name_format from ipalib.dns import record_name_format
from ipapython.dnsutil import DNSName, resolve_rrsets from ipapython.dnsutil import DNSName
from ipaserver.install import installutils
if six.PY3: if six.PY3:
unicode=str unicode=str
@@ -55,7 +55,7 @@ IPA_DEFAULT_NTP_SRV_REC = (
(DNSName("_ntp._udp"), 123), (DNSName("_ntp._udp"), 123),
) )
CA_RECORDS_DNS_TIMEOUT = 30 # timeout in seconds CA_RECORDS_DNS_TIMEOUT = 15 # timeout in seconds
class IPADomainIsNotManagedByIPAError(Exception): class IPADomainIsNotManagedByIPAError(Exception):
@@ -139,16 +139,20 @@ class IPASystemRecords:
def __add_ca_records_from_hostname(self, zone_obj, hostname): def __add_ca_records_from_hostname(self, zone_obj, hostname):
assert isinstance(hostname, DNSName) and hostname.is_absolute() assert isinstance(hostname, DNSName) and hostname.is_absolute()
r_name = DNSName('ipa-ca') + self.domain_abs r_name = DNSName('ipa-ca') + self.domain_abs
rrsets = [] rrsets = None
end_time = time() + CA_RECORDS_DNS_TIMEOUT end_time = time() + CA_RECORDS_DNS_TIMEOUT
while time() < end_time: while True:
try: try:
rrsets = resolve_rrsets(hostname, (rdatatype.A, rdatatype.AAAA)) # function logs errors
except DNSException: # logging is done inside resolve_rrsets rrsets = installutils.resolve_rrsets_nss(hostname)
except OSError:
# also retry on EAI_AGAIN, EAI_FAIL
pass pass
if rrsets: if rrsets:
break break
sleep(5) if time() >= end_time:
break
sleep(3)
if not rrsets: if not rrsets:
logger.error('unable to resolve host name %s to IP address, ' logger.error('unable to resolve host name %s to IP address, '

View File

@@ -39,7 +39,7 @@ from contextlib import contextmanager
from configparser import ConfigParser as SafeConfigParser from configparser import ConfigParser as SafeConfigParser
from configparser import NoOptionError from configparser import NoOptionError
from dns import rdatatype from dns import rrset, rdatatype, rdataclass
from dns.exception import DNSException from dns.exception import DNSException
import ldap import ldap
import six import six
@@ -55,7 +55,7 @@ from ipalib.util import validate_hostname
from ipalib import api, errors, x509 from ipalib import api, errors, x509
from ipalib.install import dnsforwarders from ipalib.install import dnsforwarders
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.dnsutil import resolve from ipapython.dnsutil import DNSName, resolve
from ipaserver.install import certs, service, sysupgrade from ipaserver.install import certs, service, sysupgrade
from ipaplatform import services from ipaplatform import services
from ipaplatform.paths import paths from ipaplatform.paths import paths
@@ -480,6 +480,40 @@ def resolve_ip_addresses_nss(fqdn):
return ip_addresses return ip_addresses
def resolve_rrsets_nss(fqdn):
"""Get list of dnspython RRsets from NSS"""
if not isinstance(fqdn, DNSName):
fqdn = DNSName.from_text(fqdn)
ip_addresses = resolve_ip_addresses_nss(fqdn.to_text())
# split IP addresses into IPv4 and IPv6
ipv4 = []
ipv6 = []
for ip_address in ip_addresses:
if ip_address.version == 4:
ipv4.append(str(ip_address))
elif ip_address.version == 6:
ipv6.append(str(ip_address))
# construct an RRset for each address type. TTL is irrelevant
ttl = 3600
rrs = []
if ipv4:
rrs.append(
rrset.from_text_list(
fqdn, ttl, rdataclass.IN, rdatatype.A, ipv4
)
)
if ipv6:
rrs.append(
rrset.from_text_list(
fqdn, ttl, rdataclass.IN, rdatatype.AAAA, ipv6
)
)
return rrs
def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses): def get_server_ip_address(host_name, unattended, setup_dns, ip_addresses):
hostaddr = resolve_ip_addresses_nss(host_name) hostaddr = resolve_ip_addresses_nss(host_name)
if hostaddr.intersection( if hostaddr.intersection(