On Sat, 2011-05-28 at 10:13 +0200, Martin Kosek wrote:
> On Sat, 2011-05-28 at 00:10 -0400, Rob Crittenden wrote:
> > Martin Kosek wrote:
> > > On Mon, 2011-05-23 at 16:41 -0400, Rob Crittenden wrote:
> > >> Martin Kosek wrote:
> > >>> This is a first version of connection checking program for replica
> > >>> installation. See patch for program purpose description. Currently,
> > >>> there is no man pages for the program.
> > >>>
> > >>> Note to Simo and Rob: I use password for logging as admin. Btw would it
> > >>> be safe to have an admin keytab in the replica file? Replica file
> > >>> contents are lying freely in /tmp after the replica installation.
> > >>>
> > >>> Martin
> > >>
> > >> nack, you aren't including the new binary in the spec.
> > >
> > > Oh, thanks for this one.
> > >
> > >>
> > >> You should also:
> > >>
> > >> - set KRB5CCNAME to a temporary ccache and remove that when the install
> > >> exists (successful or not)
> > >
> > > Done.
> > >
> > >> - remove the temporary krb5.conf you create
> > >
> > > Done.
> > >
> > >> - be a bit more explicit what we are doing, at least more than "Run
> > >> connection check to master".
> > >
> > > Actually, I am if you run the new script separately. I removed "--quiet"
> > > parameter passed to the script in ipa-replica-install so that it is more
> > > verbose. Plus, I improved texts sent to the user.
> > >
> > >> - yes, we should remove the replica file contents
> > >
> > > I enhanced ipa-replica-install to do that.
> > >
> > > Martin
> > >
> > 
> > Works great until the very end:
> > ...
> > ...
> > 
> > Execute check on remote master
> > Check connection from master to remote replica 'slinky.greyoak.com':
> >     Directory Service: unsecure port (389): FAILED
> >     Directory Service: secure port (636): FAILED
> >     Kerberos (88): OK
> > 
> > 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! Unaccessible port(s): 389, 636
> > 
> > Connection check failed with following error: None
> > 
> > rob
> 
> Right, I introduced this wrong error message in the last patch. I fixed
> this one and also one typo. Updated patch attached.
> 
> Martin

I created a man page for the new program. Please feel free to
fix/propose a fix for any language errors that may be there.

Missing records in Makefile.am for both man page and the new program
have been added.

Martin
>From a9f7130c221d9657713b5f1140b1c745d2857140 Mon Sep 17 00:00:00 2001
From: Martin Kosek <mko...@redhat.com>
Date: Sun, 22 May 2011 19:17:07 +0200
Subject: [PATCH] Connection check program for replica installation

When connection between a master machine and future replica is not
sane, the replica installation may fail unexpectedly with
inconvenient error messages. One common problem is misconfigured
firewall.

This patch adds a program ipa-replica-conncheck which tests the
connection using the following procedure:

1) Execute the on-replica check testing the connection to master
2) Open required ports on local machine
3) Ask user to run the on-master part of the check OR run it
   automatically:
     a) kinit to master as default admin user with given password
     b) run the on-master part using ssh
4) When master part is executed, it checks connection back to
   the replica and prints the check result

This program is run by ipa-replica-install as mandatory part. It
can, however, be skipped using --skip-conncheck option.
ipa-replica-install now requires password for admin user to run
the command on remote master.

https://fedorahosted.org/freeipa/ticket/1107
---
 freeipa.spec.in                           |    2 +
 install/po/Makefile.in                    |    1 +
 install/tools/Makefile.am                 |    1 +
 install/tools/ipa-replica-conncheck       |  372 +++++++++++++++++++++++++++++
 install/tools/ipa-replica-install         |   40 +++
 install/tools/man/Makefile.am             |    1 +
 install/tools/man/ipa-replica-conncheck.1 |   87 +++++++
 install/tools/man/ipa-replica-install.1   |    6 +
 ipapython/ipautil.py                      |   73 ++++++
 9 files changed, 583 insertions(+), 0 deletions(-)
 create mode 100755 install/tools/ipa-replica-conncheck
 create mode 100644 install/tools/man/ipa-replica-conncheck.1

diff --git a/freeipa.spec.in b/freeipa.spec.in
index fba2f31e5586457c74d84430d46e57190891d7d6..5c6c8a562e8c12791c059f96916c5519368385d2 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -358,6 +358,7 @@ fi
 %doc COPYING README Contributors.txt
 %{_sbindir}/ipa-dns-install
 %{_sbindir}/ipa-server-install
+%{_sbindir}/ipa-replica-conncheck
 %{_sbindir}/ipa-replica-install
 %{_sbindir}/ipa-replica-prepare
 %{_sbindir}/ipa-replica-manage
@@ -425,6 +426,7 @@ fi
 %dir %{_localstatedir}/cache/ipa
 %attr(700,apache,apache) %dir %{_localstatedir}/cache/ipa/sessions
 %attr(700,root,root) %dir %{_localstatedir}/cache/ipa/kpasswd
+%{_mandir}/man1/ipa-replica-conncheck.1.gz
 %{_mandir}/man1/ipa-replica-install.1.gz
 %{_mandir}/man1/ipa-replica-manage.1.gz
 %{_mandir}/man1/ipa-replica-prepare.1.gz
diff --git a/install/po/Makefile.in b/install/po/Makefile.in
index 0d0fa640e5566da00e10af3190e78de24c0bdb9c..a1e804f9d89aad61537d761f85b7d82bddb93357 100644
--- a/install/po/Makefile.in
+++ b/install/po/Makefile.in
@@ -44,6 +44,7 @@ PY_EXPLICIT_FILES = \
      ipa \
      install/tools/ipa-replica-manage \
      install/tools/ipa-server-certinstall \
+     install/tools/ipa-replica-conncheck \
      install/tools/ipa-replica-install \
      install/tools/ipa-nis-manage \
      install/tools/ipa-upgradeconfig \
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index 055a32fcabffc7177713fa8d9ba499dbd523383b..9004bb248d9ace2c683f04d15da85b401411f93d 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -7,6 +7,7 @@ SUBDIRS = 			\
 sbin_SCRIPTS =			\
 	ipa-dns-install		\
 	ipa-server-install	\
+	ipa-replica-conncheck	\
 	ipa-replica-install	\
 	ipa-replica-prepare	\
 	ipa-replica-manage	\
diff --git a/install/tools/ipa-replica-conncheck b/install/tools/ipa-replica-conncheck
new file mode 100755
index 0000000000000000000000000000000000000000..63162f36c8cd1c2e5cc404b6a35948ee84f92972
--- /dev/null
+++ b/install/tools/ipa-replica-conncheck
@@ -0,0 +1,372 @@
+#! /usr/bin/python -E
+# Authors: Martin Kosek <mko...@redhat.com>
+#
+# Copyright (C) 2011  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+
+from ipapython.config import IPAOptionParser
+from ipapython import version
+from ipapython import ipautil
+from ipapython.ipautil import CalledProcessError
+import ipaclient.ipachangeconf
+from optparse import OptionGroup
+import logging
+import sys
+import os
+import signal
+import tempfile
+import getpass
+import socket
+import time
+import threading
+import errno
+
+CONNECT_TIMEOUT = 5
+RESPONDERS = [ ]
+QUIET = False
+CCACHE_FILE = "/etc/ipa/.conncheck_ccache"
+KRB5_CONFIG = None
+
+class CheckedPort(object):
+    def __init__(self, port, stream, description):
+        self.port = port
+        self.stream = stream
+        self.description = description
+
+BASE_PORTS = [ 
+                CheckedPort(389, True, "Directory Service: unsecure port"),
+                CheckedPort(636, True, "Directory Service: secure port"),
+                CheckedPort(88, False, "Kerberos"),
+             ]
+
+CA_PORTS  = [
+                CheckedPort(7389, True, "PKI-CA: Directory Service"),
+                CheckedPort(9444, True, "PKI-CA: EE Secure port"),
+                CheckedPort(9445, True, "PKI-CA: Admin Secure port"),
+                CheckedPort(9446, True, "PKI-CA: EE Secure Client Auth port"),
+                CheckedPort(9180, True, "PKI-CA: Unsecure port"),
+            ]
+
+def print_info(msg):
+    if not QUIET:
+        print msg
+
+def parse_options():
+    parser = IPAOptionParser(version=version.VERSION)
+
+    replica_group = OptionGroup(parser, "on-replica options")
+    replica_group.add_option("-m", "--master", dest="master",
+                      help="Master address with running IPA for output connection check")
+    replica_group.add_option("-a", "--auto-master-check", dest="auto_master_check",
+                      action="store_true",
+                      default=False,
+                      help="Automatically execute connection check on master")
+    replica_group.add_option("-r", "--realm", dest="realm",
+                      help="Realm name")
+    replica_group.add_option("-k", "--kdc", dest="kdc",
+                      help="Master KDC. Defaults to master address")
+    replica_group.add_option("-p", "--principal", dest="principal",
+                      default="admin", help="Principal to use to log in to remote master")
+    replica_group.add_option("-w", "--password", dest="password", sensitive=True,
+                      help="Password for the principal"),
+    parser.add_option_group(replica_group)
+
+
+    master_group = OptionGroup(parser, "on-master options")
+    master_group.add_option("-R", "--replica", dest="replica",
+                      help="Address of remote replica machine to check against")
+    parser.add_option_group(master_group)
+
+    common_group = OptionGroup(parser, "common options")
+    common_group.add_option("-c", "--check-ca", dest="check_ca",
+                      action="store_true",
+                      default=False,
+                      help="Check also ports for Certificate Authority")
+
+    common_group.add_option("", "--hostname", dest="hostname",
+                      help="The hostname of this server (FQDN). "
+                           "By default a nodename from uname(2) is used.")
+    parser.add_option_group(common_group)
+
+    parser.add_option("-d", "--debug", dest="debug",
+                      action="store_true",
+                      default=False, help="Print debugging information")
+    parser.add_option("-q", "--quiet", dest="quiet",
+                      action="store_true",
+                      default=False, help="Output only errors")
+
+    options, args = parser.parse_args()
+    safe_options = parser.get_safe_opts(options)
+
+    if options.master and options.replica:
+        parser.error("on-master and on-replica options are mutually exclusive!")
+
+    if options.master:
+        if options.auto_master_check and not options.realm:
+            parser.error("Realm is parameter is required to connect to remote master!")
+        if not os.getegid() == 0:
+            parser.error("You can only run on-replica part as root.")
+
+    if options.master and not options.kdc:
+       options.kdc = options.master
+
+    if not options.master and not options.replica:
+       parser.error("No action: you should select either --replica or --master option.")
+
+    if not options.hostname:
+        options.hostname = socket.getfqdn()
+
+    if options.quiet:
+        global QUIET
+        QUIET = True
+
+    return safe_options, options
+
+def logging_setup(options):
+    if os.getegid() == 0:
+        log_file = "/var/log/ipareplica-conncheck.log"
+        old_umask = os.umask(077)
+        logging.basicConfig(level=logging.DEBUG,
+                        format='%(asctime)s %(levelname)s %(message)s',
+                        filename=log_file,
+                        filemode='w')
+        os.umask(old_umask)
+
+    console = logging.StreamHandler()
+    # If the debug option is set, also log debug messages to the console
+    if options.debug:
+        console.setLevel(logging.DEBUG)
+    else:
+        # Otherwise, log critical and error messages
+        console.setLevel(logging.ERROR)
+    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
+    console.setFormatter(formatter)
+    logging.getLogger('').addHandler(console)
+
+def clean_responders(responders):
+    if not responders:
+        return
+
+    for responder in responders:
+        responder.stop()
+
+    for responder in responders:
+        responder.join()
+        responders.remove(responder)
+
+def sigterm_handler(signum, frame):
+    print_info("\nCleaning up...")
+
+    global RESPONDERS
+    clean_responders(RESPONDERS)
+
+    sys.exit(1)
+
+def configure_krb5_conf(realm, kdc, filename):
+
+    krbconf = ipaclient.ipachangeconf.IPAChangeConf("IPA Installer")
+    krbconf.setOptionAssignment(" = ")
+    krbconf.setSectionNameDelimiters(("[","]"))
+    krbconf.setSubSectionDelimiters(("{","}"))
+    krbconf.setIndent(("","  ","    "))
+
+    opts = [{'name':'comment', 'type':'comment', 'value':'File created by ipa-replica-conncheck'},
+            {'name':'empty', 'type':'empty'}]
+
+    #[libdefaults]
+    libdefaults = [{'name':'default_realm', 'type':'option', 'value':realm}]
+    libdefaults.append({'name':'dns_lookup_realm', 'type':'option', 'value':'false'})
+    libdefaults.append({'name':'dns_lookup_kdc', 'type':'option', 'value':'false'})
+    libdefaults.append({'name':'rdns', 'type':'option', 'value':'false'})
+    libdefaults.append({'name':'ticket_lifetime', 'type':'option', 'value':'24h'})
+    libdefaults.append({'name':'forwardable', 'type':'option', 'value':'yes'})
+
+    opts.append({'name':'libdefaults', 'type':'section', 'value': libdefaults})
+    opts.append({'name':'empty', 'type':'empty'})
+
+    #the following are necessary only if DNS discovery does not work
+    #[realms]
+    realms_info =[{'name':'kdc', 'type':'option', 'value':kdc+':88'},
+                 {'name':'admin_server', 'type':'option', 'value':kdc+':749'}]
+    realms = [{'name':realm, 'type':'subsection', 'value':realms_info}]
+
+    opts.append({'name':'realms', 'type':'section', 'value':realms})
+    opts.append({'name':'empty', 'type':'empty'})
+
+    #[appdefaults]
+    pamopts = [{'name':'debug', 'type':'option', 'value':'false'},
+               {'name':'ticket_lifetime', 'type':'option', 'value':'36000'},
+               {'name':'renew_lifetime', 'type':'option', 'value':'36000'},
+               {'name':'forwardable', 'type':'option', 'value':'true'},
+               {'name':'krb4_convert', 'type':'option', 'value':'false'}]
+    appopts = [{'name':'pam', 'type':'subsection', 'value':pamopts}]
+    opts.append({'name':'appdefaults', 'type':'section', 'value':appopts})
+
+    logging.debug("Writing temporary Kerberos configuration to %s:\n%s"
+            % (filename, krbconf.dump(opts)))
+
+    krbconf.newConf(filename, opts)
+
+class PortResponder(threading.Thread):
+
+    def __init__(self, port, socket_stream = True, socket_timeout=1):
+        super(PortResponder, self).__init__()
+        self.port = port
+        self.socket_stream = socket_stream
+        self.socket_timeout = socket_timeout
+        self._stop_request = False
+
+    def run(self):
+        while not self._stop_request:
+            try:
+                ipautil.bind_port_responder(self.port, self.socket_stream,
+                        self.socket_timeout, responder_data="FreeIPA")
+            except socket.timeout:
+                pass
+            except socket.error, e:
+                if e.errno == errno.EADDRINUSE:
+                    time.sleep(1)
+                else:
+                    raise
+
+    def stop(self):
+        self._stop_request = True
+
+def port_check(host, port_list):
+    failed_ports = []
+    for port in port_list:
+        if ipautil.host_port_open(host, port.port, port.stream, CONNECT_TIMEOUT):
+            result = "OK"
+        else:
+            failed_ports.append(port)
+            result = "FAILED"
+        print_info("   %s (%d): %s" % (port.description, port.port, result))
+    
+    if failed_ports:
+        msg_ports = ", ".join([str(port.port) for port in failed_ports])
+        raise RuntimeError("Port check failed! Inaccessible port(s): %s" % msg_ports)
+
+def main():
+    safe_options, options = parse_options()
+
+    logging_setup(options)
+    logging.debug('%s was invoked with options: %s' % (sys.argv[0], safe_options))
+    logging.debug("missing options might be asked for interactively later\n")
+
+    signal.signal(signal.SIGTERM, sigterm_handler)
+    signal.signal(signal.SIGINT, sigterm_handler)
+
+    required_ports = BASE_PORTS
+    if options.check_ca:
+        required_ports.extend(CA_PORTS)
+
+    if options.replica:
+        print_info("Check connection from master to remote replica '%s':" % options.replica)
+        port_check(options.replica, required_ports)
+        print_info("\nConnection from master to replica is OK.")
+
+    # kinit to foreign master
+    if options.master:
+        # check ports on master first
+        print_info("Check connection from replica to remote master '%s':" % options.master)
+        port_check( options.master, required_ports)
+        print_info("\nConnection from replica to master is OK.")
+
+        # create listeners
+        global RESPONDERS
+        print_info("Start listening on required ports for remote master check")
+        for port in required_ports:
+            logging.debug("Start listening on port %d (%s)" % (port.port, port.description))
+            responder = PortResponder(port.port, port.stream)
+            responder.start()
+            RESPONDERS.append(responder)
+
+        if options.auto_master_check:
+            (krb_fd, krb_name) = tempfile.mkstemp()
+            os.close(krb_fd)
+            configure_krb5_conf(options.realm, options.kdc, krb_name)
+            global KRB5_CONFIG
+            KRB5_CONFIG = krb_name
+
+            print_info("Get credentials to log in to remote master")
+            if options.principal.find('@') == -1:
+                principal = '%s@%s' % (options.principal, options.realm)
+                user = options.principal
+            else:
+                principal = options.principal
+                user = options.principal.partition('@')[0]
+            
+            if options.password:
+                password=options.password
+            else:
+                password = getpass.getpass("Password for %s: " % principal)
+            
+            stderr=''
+            (stdout, stderr, returncode) = ipautil.run(['/usr/bin/kinit', principal],
+                 env={'KRB5_CONFIG':KRB5_CONFIG, 'KRB5CCNAME':CCACHE_FILE},
+                 stdin=password, raiseonerr=False)
+            if returncode != 0:
+                raise RuntimeError("Cannot acquire Kerberos ticket: %s" % stderr)
+
+            remote_check_opts = ['--replica %s' % options.hostname]
+            if options.check_ca:
+                remote_check_opts.append('--check-ca')
+
+            print_info("Execute check on remote master")
+            
+            stderr = ''
+            remote_addr = "%s@%s" % (user, options.master)
+            (stdout, stderr, returncode) = ipautil.run(['/usr/bin/ssh', remote_addr,
+                "/usr/sbin/ipa-replica-conncheck " + " ".join(remote_check_opts)],
+                env={'KRB5_CONFIG':KRB5_CONFIG, 'KRB5CCNAME' : CCACHE_FILE},
+                raiseonerr=False)
+
+            print_info(stdout)
+
+            if returncode != 0:
+                raise RuntimeError("Remote master check failed with following error message(s):\n%s" % stderr)
+        else:
+            # wait until user  test is ready
+            print_info("Listeners are started. Use CTRL+C to terminate the listening part after the test.")
+            print_info("")
+            print_info("Please run the following command on remote master:")
+
+            remote_check_opts = ['--replica %s' % options.hostname]
+            if options.check_ca:
+                remote_check_opts.append('--check-ca')
+            print_info("/usr/sbin/ipa-replica-conncheck " + " ".join(remote_check_opts))
+            time.sleep(3600)
+            print_info("Connection check timeout: terminating listening program")
+
+if __name__ == "__main__":
+    try:
+        sys.exit(main())
+    except SystemExit, e:
+        sys.exit(e)
+    except KeyboardInterrupt:
+        sys.exit(1)
+    except RuntimeError, e:
+        sys.exit(e)
+    finally:
+        clean_responders(RESPONDERS)
+        for file_name in (CCACHE_FILE, KRB5_CONFIG):
+            if file_name:
+                try:
+                    os.remove(file_name)
+                except OSError:
+                    pass
+
diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 2848366ddb689a70c30f3a9781238a1a428a660d..248d632856b5c895817221f5d904499ce5ce9710 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -38,6 +38,7 @@ from ipapython.config import IPAOptionParser
 from ipapython import sysrestore
 
 CACERT="/etc/ipa/ca.crt"
+REPLICA_INFO_TOP_DIR=None
 
 class ReplicaConfig:
     def __init__(self):
@@ -58,6 +59,8 @@ def parse_options():
                       default=False, help="gather extra debugging information")
     parser.add_option("-p", "--password", dest="password", sensitive=True,
                       help="Directory Manager (existing master) password")
+    parser.add_option("-w", "--admin-password", dest="admin_password", sensitive=True,
+                      help="Admin user Kerberos password used for connection check")
     parser.add_option("--setup-dns", dest="setup_dns", action="store_true",
                       default=False, help="configure bind with our zone")
     parser.add_option("--forwarder", dest="forwarders", action="append",
@@ -71,6 +74,8 @@ def parse_options():
                       help="Do not use DNS for hostname lookup during installation")
     parser.add_option("--no-pkinit", dest="setup_pkinit", action="store_false",
                       default=True, help="disables pkinit setup steps")
+    parser.add_option("--skip-conncheck", dest="skip_conncheck", action="store_true",
+                      default=False, help="skip connection check to remote master")
     parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
                       default=False, help="unattended installation never prompts the user")
 
@@ -388,6 +393,8 @@ def main():
 
     try:
         top_dir, dir = expand_info(filename, dirman_password)
+        global REPLICA_INFO_TOP_DIR
+        REPLICA_INFO_TOP_DIR = top_dir
     except Exception, e:
         print "ERROR: Failed to decrypt or open the replica file."
         print "Verify you entered the correct Directory Manager password."
@@ -408,6 +415,32 @@ def main():
             sys.exit(0)
     config.dir = dir
 
+
+    # check connection
+    if not options.skip_conncheck:
+        print "Run connection check to master"
+        args = ["/usr/sbin/ipa-replica-conncheck", "--master", config.master_host_name,
+                "--auto-master-check", "--realm", config.realm_name,
+                "--principal", "admin",
+                "--hostname", config.host_name]
+
+        if options.admin_password:
+            args.extend(["--password", options.admin_password])
+
+        cafile = config.dir + "/cacert.p12"
+        if ipautil.file_exists(cafile): # with CA
+            args.append('--check-ca')
+        logging.debug("Running ipa-replica-conncheck with following arguments: %s" %
+                " ".join(args))
+        (stdin, stderr, returncode) = ipautil.run(args,raiseonerr=False, capture_output=False)
+
+        if returncode != 0:
+            sys.exit("Connection check failed!" + 
+                     "\nPlease fix your network settings according to error messages above." +
+                     "\nIf the check results are not valid it can be skipped with --skip-conncheck parameter.")
+        else:
+            print "Connection check OK"
+
     # Create the management framework config file
     # Note: We must do this before bootstraping and finalizing ipalib.api
     fd = open("/etc/ipa/default.conf", "w")
@@ -555,6 +588,13 @@ except Exception, e:
     logging.debug(message)
 except KeyboardInterrupt:
     print "Installation cancelled." 
+finally:
+    # always try to remove decrypted replica file
+    try:
+        if REPLICA_INFO_TOP_DIR:
+            shutil.rmtree(REPLICA_INFO_TOP_DIR)
+    except OSError:
+        pass
 
 print ""
 print "Your system may be partly configured." 
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
index 3fac378c9ef834d3b1e366bc45c32dc255bad400..be2524e865fa89165a3023eb2d3c04043d694165 100644
--- a/install/tools/man/Makefile.am
+++ b/install/tools/man/Makefile.am
@@ -5,6 +5,7 @@ AUTOMAKE_OPTIONS = 1.7
 NULL=
 
 man1_MANS = 				\
+	ipa-replica-conncheck.1		\
 	ipa-replica-install.1		\
 	ipa-replica-manage.1		\
 	ipa-replica-prepare.1		\
diff --git a/install/tools/man/ipa-replica-conncheck.1 b/install/tools/man/ipa-replica-conncheck.1
new file mode 100644
index 0000000000000000000000000000000000000000..d47db203401766943982e5a5ee0e3a5cc15dbb21
--- /dev/null
+++ b/install/tools/man/ipa-replica-conncheck.1
@@ -0,0 +1,87 @@
+.\" A man page for ipa-replica-conncheck
+.\" Copyright (C) 2011 Red Hat, Inc.
+.\" 
+.\" This program is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation, either version 3 of the License, or
+.\" (at your option) any later version.
+.\" 
+.\" This program is distributed in the hope that it will be useful, but
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+.\" General Public License for more details.
+.\" 
+.\" You should have received a copy of the GNU General Public License
+.\" along with this program.  If not, see <http://www.gnu.org/licenses/>.
+.\" 
+.\" Author: Martin Kosek <mko...@redhat.com>
+.\" 
+.TH "ipa-replica-conncheck" "1" "Jun 2 2011" "freeipa" ""
+.SH "NAME"
+ipa\-replica\-conncheck \- Check a replica\-master network connection before installation
+.SH "SYNOPSIS"
+ipa\-replica\-conncheck [\fIOPTION\fR]...
+.SH "DESCRIPTION"
+When an IPA replica is being installed a network connection between a replica machine and a replicated IPA master machine has to be prepared for master\-replica communication. In case of a flawed connection the installation may fail with inconvenient error messages. A common connection problem is a misconfigured firewall with closed required port on a replica or master machine.
+
+The connection is checked by running a set of tests from both master and replica machines. The program is incorporated to ipa\-replica\-install(1) but can be also run separately.
+
+.SH "OPTIONS"
+.SS "REPLICA MACHINE OPTIONS"
+This set of options is used when the connection check is run on a prepared IPA replica machine.
+.TP 
+\fB\-m\fR \fIMASTER\fR, \fB\-\-master\fR=\fIMASTER\fR
+Remote master machine address
+.TP 
+\fB\-a\fR, \fB\-\-auto\-master\-check\fR
+Automatically log in to master machine and execute the master machine part of the connection check. The following options for replica part are only evaluated when this option is set
+.TP 
+\fB\-r\fR \fIREALM\fR, \fB\-\-realm\fR=\fIREALM\fR
+The Kerberos realm name for the IPA server
+.TP
+\fB\-k\fR \fIKDC\fR, \fB\-\-kdc\fR=\fIKDC\fR
+KDC server address. Defaults t \fIMASTER\fR
+.TP
+\fB\-p\fR \fIPRINCIPAL\fR, \fB\-\-principal\fR=\fIPRINCIPAL\fR
+Authorized Kerberos principal to use to log in to master machine. Defaults to \fIadmin\fR
+.TP
+\fB\-w\fR \fIPASSWORD\fR, \fB\-\-password\fR=\fIPASSWORD\fR
+Password for given principal. The password will be prompted interactively when this option is missing
+
+.SS "MASTER MACHINE OPTIONS"
+This set of options is used when the connection check is run on a master machine against a running ipa\-replica\-conncheck(1) on a replica machine.
+.TP
+\fB\-R\fR \fIREPLICA\fR, \fB\-\-replica\fR=\fIREPLICA\fR
+Remote replica machine address
+
+.SS "COMMON OPTIONS"
+.TP
+\fB\-c\fR, \fB\-\-check\-ca\fR
+Include in a check also a set of dogtag connection requirements. When a replica is self\-sign this option is not needed.
+.TP
+\fB\-h\fR \fIHOSTNAME\fR, \fB\-\-hostname\fR=\fIHOSTNAME\fR
+The hostname of this server (FQDN). By default a nodename from uname(2) is used
+.TP
+\fB\-d\fR, \fB\-\-debug\fR
+Print debugging information
+.TP
+\fB\-q\fR, \fB\-\-quiet\fR
+Output only errors
+
+.SH "EXAMPLES"
+.TP
+\fBipa-replica-conncheck -m master.example.com\fR
+Run a replica machine connection check against a remote master \fImaster.example.com\fR. If the connection to the remote master machine is successful the program will switch to listening mode and prompt for running the master machine part. The second part check the connection from master to replica.
+.TP
+\fBipa-replica-conncheck -R replica.example.com\fR
+Run a master machine connection check part. This is either run automatically by replica part of the connection check program (when \fI-a\fR option is set) or manually by the user. A running ipa-replica-conncheck(1) in a listening mode must be already running on a replica machine.
+.TP
+\fBipa-replica-conncheck -m master.example.com -a -r EXAMPLE.COM -w password\fR
+Run a replica\-master connection check. In case of a success switch to listening mode, automatically log to \fImaster.example.com\fR in a realm \fIEXAMPLE.COM\fR with a password \fIpassword\fR and run the second part of the connection check.
+
+.SH "EXIT STATUS"
+0 if the connection check was successful
+
+1 if an error occurred
+.SH "SEE ALSO"
+.BR ipa-replica-install (1)
diff --git a/install/tools/man/ipa-replica-install.1 b/install/tools/man/ipa-replica-install.1
index 3ee304224bb6db249b74cca736e95bba2c4356af..8889235462ef52327c951714ab62982b5003c8cc 100644
--- a/install/tools/man/ipa-replica-install.1
+++ b/install/tools/man/ipa-replica-install.1
@@ -36,6 +36,9 @@ Enable debug logging when more verbose output is needed
 \fB\-p\fR, \fB\-\-password\fR=\fIDM_PASSWORD\fR
 Directory Manager (existing master) password
 .TP
+\fB\-w\fR \fIADMIN_PASSWORD\fR, \fB\-\-admin\-password\fR=\fIADMIN_PASSWORD\fR
+Admin user Kerberos password used for connection check
+.TP
 \fB\-\-setup\-dns\fR
 Generate a DNS zone if it does not exist already and configure the DNS server.
 This option requires that you either specify at least one DNS forwarder through
@@ -58,6 +61,9 @@ Do not use DNS for hostname lookup during installation
 \fB\-\-no\-pkinit\fR
 Disables pkinit setup steps
 .TP
+\fB\-\-skip\-conncheck\fR
+Skip connection check to remote master
+.TP
 \fB\-U\fR, \fB\-\-unattended\fR
 An unattended installation that will never prompt for user input
 .SH "EXIT STATUS"
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index acfd70cae8988b908295313df85409cfdb693dfe..2c50019a0f77548fc9603f1be8ba7da841ca6fde 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -32,6 +32,7 @@ import copy
 import stat
 import shutil
 import urllib2
+import socket
 
 from ipapython import ipavalidate
 from types import *
@@ -1093,3 +1094,75 @@ def chkconfig_add(service_name):
 def chkconfig_del(service_name):
     run(["/sbin/chkconfig", "--del", service_name])
 
+def host_port_open(host, port, socket_stream=True, socket_timeout=None):
+    families = (socket.AF_INET, socket.AF_INET6)
+    success = False
+
+    if socket_stream:
+        socket_type = socket.SOCK_STREAM
+    else:
+        socket_type = socket.SOCK_DGRAM
+
+    for family in families:
+        try:
+            try:
+                s = socket.socket(family, socket_type)
+            except socket.error:
+                continue
+
+            if socket_timeout is not None:
+                s.settimeout(socket_timeout)
+            
+            s.connect((host, port))
+            success = True
+        except socket.error, e:
+            pass
+        finally:
+            s.close()
+        
+        if success:
+            return True
+
+    return False
+
+def bind_port_responder(port, socket_stream=True, socket_timeout=None, responder_data=None):
+    families = (socket.AF_INET, socket.AF_INET6)
+
+    if socket_stream:
+        socket_type = socket.SOCK_STREAM
+    else:
+        socket_type = socket.SOCK_DGRAM
+
+    host = ''   # all available interfaces
+
+    for family in families:
+        try:
+            s = socket.socket(family, socket_type)
+        except socket.error, e:
+            if family == families[-1]:  # last available family
+                raise e
+
+    if socket_timeout is not None:
+        s.settimeout(socket_timeout)
+
+    if socket_stream:
+        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 
+
+    try:
+        s.bind((host, port))
+
+        if socket_stream:
+            s.listen(1)
+            connection, client_address = s.accept()
+            try:
+                if responder_data:
+                    connection.sendall(responder_data) #pylint: disable=E1101
+            finally:
+                connection.close()
+        else:
+            data, addr = s.recvfrom( 512 ) # buffer size is 1024 bytes
+
+            if responder_data:
+                s.sendto(responder_data, addr)
+    finally:
+        s.close()
-- 
1.7.5.2

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

Reply via email to