On 09/19/2012 04:56 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
On 09/17/2012 08:10 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
On 09/14/2012 08:46 AM, Martin Kosek wrote:
On 09/13/2012 10:35 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
On 09/11/2012 11:05 PM, Rob Crittenden wrote:
Petr Viktorin wrote:
On 09/04/2012 07:44 PM, Rob Crittenden wrote:
Petr Viktorin wrote:


Shouldn't this also call verify_fqdn() on the local hostname and
just the master? I think this would eventually fail in the
what if that was skipped?


A few lines above there is a call to get_host_name, which will

I double-checked this, it fails in conncheck. Here are my steps:

# ipa-server-install --setup-dns
# ipa-replica-prepare replica.example.com
# ipa host-del replica.example.com

On replica, set DNS to IPA master, with hostname in /etc/hosts.

# ipa-replica-install ...

The verify_fqdn() passes because the resolver uses /etc/hosts.

The conncheck fails:

Execute check on remote master
Check connection from master to remote replica

Remote master check failed with following error message(s):
Could not chdir to home directory /home/admin: No such file or
Port check failed! Unable to resolve host name

Connection check failed!
Please fix your network settings according to error messages above.
If the check results are not valid it can be skipped with
--skip-conncheck parameter.

The DNS test happens much further after this, and I get why, I just
don't see how useful it is unless the --skip-conncheck is used.

For the record, it's because we need to check if the host has DNS
installed. We need a LDAP connection to check this.

ipa-replica-install ~rcrit/replica-info-replica.example.com.gpg
Directory Manager (existing master) password:

ipa         : ERROR    Could not resolve hostname
using DNS. Clients may not function properly. Please check your DNS
setup. (Note that this check queries IPA DNS directly and ignores
Continue? [no]:

So I guess, what are the intentions here? It is certainly better


If the replica is in the master's /etc/hosts, but not in DNS, the
conncheck will succeed. This check explicitly queries IPA records
and ignores /etc/hosts so it'll notice this case and warn.

Ok, like I said, this is better than we have. Just one nit then you
get an ack:

+        # If remote host has DNS, check forward/reverse resolution
+        try:
+            entry = conn.find_entries(u'cn=dns',
+        except errors.NotFound:

u'cn=dns' should be str(constants.container_dns).


This is a search filter, Petr could use the one I already have in
"dns.py::get_dns_masters()" function:

For performance sake, I would also not search in the entire tree, but
limit the
search only to:

DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'), api.env.basedn)


Attaching updated patch with Martin's suggestions.

I think what Martin had in mind was:

if api.Object.dnsrecord.get_dns_masters():

I didn't want to do this because api.Object.* use our global ldap2
Backend, which is hardwired to query localhost.
I see now that I can hack around this, and we already do this in
I've extracted the hack and reused it to get the DNS masters.

I can't say I'm crazy about the method name you've chosen...


I intended the name as a warning to not use it unless necessary.

Changed to temporary_ldap2_connection.

From 875401242271d17366d0f8cda98c3df6683873ff Mon Sep 17 00:00:00 2001
From: Petr Viktorin <pvikt...@redhat.com>
Date: Tue, 4 Sep 2012 03:47:43 -0400
Subject: [PATCH] Check direct/reverse hostname/address resolution in

Forward and reverse resolution of the newly created replica is already
checked via get_host_name (which calls verify_fqdn).
Add the same check for the existing master.

Additionally, if DNS is installed on the remote host, check forward
and reverse resolution of both replicas using that DNS only
(ignoring /etc/hosts). These checks give only warnings and, in interactive
installs, a "Continue?" prompt.

 install/tools/ipa-replica-install | 156 +++++++++++++++++++++++++++++++++-----
 1 file changed, 138 insertions(+), 18 deletions(-)

diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 0378827d5ef91382fe4571bd92200dc8a898f6ff..c9b2b584e91d1cc3c0f6b7c833078690776400d6 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -24,6 +24,11 @@ import socket
 import os, pwd, shutil
 import grp
 from optparse import OptionGroup
+from contextlib import contextmanager
+import dns.resolver
+import dns.reversename
+import dns.exception
 from ipapython import ipautil
@@ -48,6 +53,7 @@ from ipapython.dn import DN
 log_file_name = "/var/log/ipareplica-install.log"
 CACERT = "/etc/ipa/ca.crt"
+DIRMAN_DN = DN(('cn', 'directory manager'))
 def parse_options():
     usage = "%prog [options] REPLICA_FILE"
@@ -208,7 +214,7 @@ def install_http(config, auto_redirect):
     return http
 def install_bind(config, options):
-    api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
+    api.Backend.ldap2.connect(bind_dn=DIRMAN_DN,
     if options.forwarders:
         forwarders = options.forwarders
@@ -238,31 +244,49 @@ def install_bind(config, options):
     print ""
+def temporary_ldap2_connection(host_name, bind_pw, bind_dn=DIRMAN_DN):
+    """Context in which the ldap2 backend is connected to the given host
+    When the context is entered, forcefully change the ldap2's URI and connect
+    with the given password.
+    When it's exited, disconnect and restore ldap2 to previous configuration.
+    Needed to use the standard IPA tools on the remote master, before the
+    DS on localhost is installed.
+    """
+    # TODO: We shouldn't have to resort to such hacks
+    cur_uri = api.Backend.ldap2.ldap_uri
+    # ldap2 is finalized at this point, so use __setattr__ directly
+    object.__setattr__(api.Backend.ldap2, 'ldap_uri',
+                       'ldaps://%s' % ipautil.format_netloc(host_name))
+    api.Backend.ldap2.connect(bind_dn=DIRMAN_DN, bind_pw=bind_pw,
+                              tls_cacertfile=CACERT)
+    yield
+    api.Backend.ldap2.disconnect()
+    #set it back to the default
+    object.__setattr__(api.Backend.ldap2, 'ldap_uri', cur_uri)
 def install_dns_records(config, options):
     if not bindinstance.dns_container_exists(config.master_host_name,
     # We have to force to connect to the remote master because we do this step
     # before our DS server is installed.
-    cur_uri = api.Backend.ldap2.ldap_uri
-    object.__setattr__(api.Backend.ldap2, 'ldap_uri',
-                       'ldaps://%s' % ipautil.format_netloc(config.master_host_name))
-    api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
-                              bind_pw=config.dirman_password,
-                              tls_cacertfile=CACERT)
-    bind = bindinstance.BindInstance(dm_password=config.dirman_password)
-    reverse_zone = bindinstance.find_reverse_zone(config.ip)
+    with temporary_ldap2_connection(
+            config.master_host_name, config.dirman_password):
+        bind = bindinstance.BindInstance(dm_password=config.dirman_password)
+        reverse_zone = bindinstance.find_reverse_zone(config.ip)
-    bind.add_master_dns_records(config.host_name, config.ip_address,
-                                config.realm_name, config.domain_name,
-                                reverse_zone, options.conf_ntp)
-    #set it back to the default
-    api.Backend.ldap2.disconnect()
-    object.__setattr__(api.Backend.ldap2, 'ldap_uri', cur_uri)
+        bind.add_master_dns_records(config.host_name, config.ip_address,
+                                    config.realm_name, config.domain_name,
+                                    reverse_zone, options.conf_ntp)
 def check_dirsrv():
     (ds_unsecure, ds_secure) = dsinstance.check_ports()
@@ -280,6 +304,86 @@ def check_bind():
         print "Aborting installation"
+def check_dns_resolution(host_name, dns_server):
+    """Check forward and reverse resolution of host_name using dns_server
+    """
+    # Point the resolver at specified DNS server
+    server_ips = list(
+            a[4][0] for a in socket.getaddrinfo(dns_server, None))
+    resolver = dns.resolver.Resolver()
+    resolver.nameservers = server_ips
+    root_logger.debug('Search DNS server %s (%s) for %s',
+        dns_server, server_ips, host_name)
+    # Get IP addresses of host_name
+    addresses = set()
+    for rtype in 'A', 'AAAA':
+        try:
+            result = resolver.query(host_name, rtype)
+        except dns.exception.DNSException:
+            rrset = []
+        else:
+            rrset = result.rrset
+        if rrset:
+            addresses.update(r.address for r in result.rrset)
+    if not addresses:
+        root_logger.error(
+            'Could not resolve hostname %s using DNS. '
+            'Clients may not function properly. '
+            'Please check your DNS setup. '
+            '(Note that this check queries IPA DNS directly and '
+            'ignores /etc/hosts.)',
+            host_name)
+        return False
+    no_errors = True
+    # Check each of the IP addresses
+    checked = set()
+    for address in addresses:
+        if address in checked:
+            continue
+        checked.add(address)
+        try:
+            root_logger.debug('Check reverse address %s (%s)',
+                address, host_name)
+            revname = dns.reversename.from_address(address)
+            rrset = resolver.query(revname, 'PTR').rrset
+        except Exception, e:
+            root_logger.debug('Check failed: %s %s', type(e).__name__, e)
+            root_logger.error(
+                'Reverse DNS resolution of address %s (%s) failed. '
+                'Clients may not function properly. '
+                'Please check your DNS setup. '
+                '(Note that this check queries IPA DNS directly and '
+                'ignores /etc/hosts.)',
+                address, host_name)
+            no_errors = False
+        else:
+            host_name_obj = dns.name.from_text(host_name)
+            if rrset:
+                names = [r.target.to_text() for r in rrset]
+            else:
+                names = []
+            root_logger.debug(
+                'Address %s resolves to: %s. ', address, ', '.join(names))
+            if not rrset or not any(
+                    r.target == host_name_obj for r in rrset):
+                root_logger.error(
+                    'The IP address %s of host %s resolves to: %s. '
+                    'Clients may not function properly. '
+                    'Please check your DNS setup. '
+                    '(Note that this check queries IPA DNS directly and '
+                    'ignores /etc/hosts.)',
+                    address, host_name, ', '.join(names))
+                no_errors = False
+    return no_errors
 def main():
     safe_options, options, filename = parse_options()
@@ -353,6 +457,7 @@ def main():
     config.dir = dir
     config.setup_ca = options.setup_ca
+    installutils.verify_fqdn(config.master_host_name, options.no_host_dns)
     # check connection
     if not options.skip_conncheck:
@@ -414,8 +519,7 @@ def main():
     ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
         conn = ldap2(shared_instance=False, ldap_uri=ldapuri, base_dn='')
-        conn.connect(bind_dn=DN(('cn', 'directory manager')),
-                     bind_pw=config.dirman_password,
+        conn.connect(bind_dn=DIRMAN_DN, bind_pw=config.dirman_password,
         replman = ReplicationManager(config.realm_name, config.master_host_name,
@@ -427,6 +531,22 @@ def main():
             found = True
         except errors.NotFound:
+        # If remote host has DNS, check forward/reverse resolution
+        with temporary_ldap2_connection(
+                config.master_host_name, config.dirman_password):
+            dns_masters = api.Object['dnsrecord'].get_dns_masters()
+        if dns_masters:
+            master = config.master_host_name
+            if not options.no_host_dns:
+                resolution_ok = (
+                    check_dns_resolution(master, master) and
+                    check_dns_resolution(config.host_name, master))
+                root_logger.debug('Check forward/reverse DNS resolution')
+                if not resolution_ok and not options.unattended:
+                    if not ipautil.user_input("Continue?", False):
+                        sys.exit(0)
             (agreement_cn, agreement_dn) = replman.agreement_dn(host)
             entry = conn.get_entry(agreement_dn, ['*'])

Freeipa-devel mailing list

Reply via email to