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:

https://fedorahosted.org/freeipa/ticket/2845

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

rob

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


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

# ipa-server-install --setup-dns
# ipa-replica-prepare replica.example.com --ip-address=192.168.100.2
# 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 'replica.example.com':

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

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
--skip-conncheck
Directory Manager (existing master) password:

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

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

rob

If the replica is in the master's /etc/hosts, but not in DNS, the
conncheck will succeed. This check explicitly queries IPA records only
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',
base_dn=DN(api.env.basedn))
+        except errors.NotFound:

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

rob

This is a search filter, Petr could use the one I already have in
"dns.py::get_dns_masters()" function:
'(&(objectClass=ipaConfigObject)(cn=DNS))'

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)

Martin


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 ipa-replica-install.
I've extracted the hack and reused it to get the DNS masters.


--
Petr³
From 5e22359342091c60717f65dae1f2a0be2df8cce8 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
 ipa-replica-install

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.

https://fedorahosted.org/freeipa/ticket/2845
---
 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 267a70d8b60d96de9a9bde83b15c81ae59da1a96..0e338e3d93c2360a46ac23b181eef124e9759808 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
 
@@ -47,6 +52,7 @@ from ipapython.dn import DN
 log_file_name = "/var/log/ipareplica-install.log"
 CACERT = "/etc/ipa/ca.crt"
 REPLICA_INFO_TOP_DIR = None
+DIRMAN_DN = DN(('cn', 'directory manager'))
 
 def parse_options():
     usage = "%prog [options] REPLICA_FILE"
@@ -207,7 +213,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,
                               bind_pw=config.dirman_password)
     if options.forwarders:
         forwarders = options.forwarders
@@ -237,31 +243,49 @@ def install_bind(config, options):
     bind.check_global_configuration()
     print ""
 
+
+@contextmanager
+def monkeypatched_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,
                                              ipautil.realm_to_suffix(config.realm_name),
                                              dm_password=config.dirman_password):
         return
 
     # 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 monkeypatched_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()
@@ -279,6 +303,86 @@ def check_bind():
         print "Aborting installation"
         sys.exit(1)
 
+
+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():
     ipaservices.check_selinux_status()
     safe_options, options, filename = parse_options()
@@ -352,6 +456,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:
@@ -411,8 +516,7 @@ def main():
     ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
     try:
         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,
                      tls_cacertfile=CACERT)
         replman = ReplicationManager(config.realm_name, config.master_host_name,
                                      config.dirman_password)
@@ -424,6 +528,22 @@ def main():
             found = True
         except errors.NotFound:
             pass
+
+        # If remote host has DNS, check forward/reverse resolution
+        with monkeypatched_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)
+
         try:
             (agreement_cn, agreement_dn) = replman.agreement_dn(host)
             entry = conn.get_entry(agreement_dn, ['*'])
-- 
1.7.11.4

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to