This series of patches for the master/4.1 branch attempts to implement some of the Rob's and Petr Vobornik's ideas which originated from a discussion on this list regarding my original patch fixing https://fedorahosted.org/freeipa/ticket/4808.

I suppose that these patches are just a first iteration, we may further discuss if this is the right thing to do.

Below is a quote from the original discussion just to get the context:

--
Martin^3 Babinsky

Martin Babinsky wrote:
On 03/02/2015 04:28 PM, Rob Crittenden wrote:
Petr Vobornik wrote:
On 01/12/2015 05:45 PM, Martin Babinsky wrote:
related to ticket https://fedorahosted.org/freeipa/ticket/4808

this patch seems to be a bit forgotten.

It works, looks fine.

One minor issue: trailing whitespaces in the man page.

I also wonder if it shouldn't be used in other tools which call kinit
with keytab:
* ipa-client-automount:434
* ipa-client-install:2591 (this usage should be fine since it's used for
server installation)
* dcerpc.py:545
* rpcserver.py: 971, 981 (armor for web ui forms base auth)

Most importantly the ipa-client-automount because it's called from
ipa-client-install (if location is specified) and therefore it might
fail during client installation.

Or also, kinit call with admin creadentials worked for the user but I
wonder if it was just a coincidence and may break under slightly
different but similar conditions.

I think that's a fine idea. In fact there is already a function that
could be extended, kinit_hostprincipal().

rob


So in principle we could add multiple TGT retries to
"kinit_hostprincipal()" and then plug this function to all the places
Petr mentioned in order to provide this functionality each time TGT is
requested using keytab.

Do I understand it correctly?


Honestly I think I'd only do the retries on client installation.  I
don't know that the other uses would really benefit or need this.

But this is an opportunity to consolidate some code, so I guess the
approach I'd take is to add an option to kinit_hostprincipal of
retries=0 so that only a single kinit is done. The client installers
would pass in some value.

This change is quite a bit more invasive but it's also early in the
release cycle so the risk will be spread out.

rob
From fda86199e97aa661e4ee3e73858966c7086a3ee0 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 6 Mar 2015 12:40:42 +0100
Subject: [PATCH 1/5] modifications to ipautil.kinit_hostprincipal

1.) the function can now perform multiple attempts to get host TGT before
failing.

2.) instead of a directory name specifiying the location of credentials cache
the function now takes the full path including ccache filename.

---
 ipapython/ipautil.py | 46 ++++++++++++++++++++++++++++++++--------------
 1 file changed, 32 insertions(+), 14 deletions(-)

diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 4116d974e620341119b56fad3cff1bda48af3bab..90a8d4035bce218c4cd000c9434125131b311dd9 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1175,27 +1175,45 @@ def wait_for_open_socket(socket_name, timeout=0):
             else:
                 raise e
 
-def kinit_hostprincipal(keytab, ccachedir, principal):
+def kinit_hostprincipal(keytab, ccache_name, principal, attempts=1):
     """
-    Given a ccache directory and a principal kinit as that user.
+    Given a ccache_name file and a principal kinit as that user.
+
+    The optional parameter 'attempts' specifies how many times the credential
+    initialization should be attempted before giving up and raising
+    StandardError.
 
     This blindly overwrites the current CCNAME so if you need to save
     it do so before calling this function.
 
     Thus far this is used to kinit as the local host.
     """
-    try:
-        ccache_file = 'FILE:%s/ccache' % ccachedir
-        krbcontext = krbV.default_context()
-        ktab = krbV.Keytab(name=keytab, context=krbcontext)
-        princ = krbV.Principal(name=principal, context=krbcontext)
-        os.environ['KRB5CCNAME'] = ccache_file
-        ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=princ)
-        ccache.init(princ)
-        ccache.init_creds_keytab(keytab=ktab, principal=princ)
-        return ccache_file
-    except krbV.Krb5Error, e:
-        raise StandardError('Error initializing principal %s in %s: %s' % (principal, keytab, str(e)))
+    curr_attempt = 0
+    ccache_file = 'FILE:%s' % ccache_name
+    root_logger.debug("Initializing principal %s" % principal)
+    while True:
+        curr_attempt += 1
+        try:
+            krbcontext = krbV.default_context()
+            ktab = krbV.Keytab(name=keytab, context=krbcontext)
+            princ = krbV.Principal(name=principal, context=krbcontext)
+            os.environ['KRB5CCNAME'] = ccache_file
+            ccache = krbV.CCache(name=ccache_file, context=krbcontext,
+                                 primary_principal=princ)
+            ccache.init(princ)
+            ccache.init_creds_keytab(keytab=ktab, principal=princ)
+            root_logger.debug("Attempt %d/%d: success"
+                              % (curr_attempt, attempts))
+            return
+        except krbV.Krb5Error, e:
+            root_logger.debug("Attempt %d/%d: failed"
+                              % (curr_attempt, attempts))
+            if curr_attempt >= attempts:
+                root_logger.debug("Maximum number of attempts (%d) reached"
+                                  % attempts)
+                raise StandardError('Error initializing principal %s in %s: %s'
+                                    % (principal, keytab, str(e)))
+            time.sleep(1)
 
 def dn_attribute_property(private_name):
     '''
-- 
2.1.0

From 4defd0a929ab49cf3509af48cbc1ae9835815120 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 6 Mar 2015 12:41:23 +0100
Subject: [PATCH 2/5] ipa-client-install: try to get host TGT several times
 before giving up

New option '--kinit-attempts' enables the host to make multiple attempts to
obtain TGT from KDC before giving up and aborting client installation.

In addition, all kinit attempts using host keytab were replaced by calls to
'ipautil.kinit_hostprincipal' function.

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

---
 ipa-client/ipa-install/ipa-client-install | 49 +++++++++++++++++++------------
 ipa-client/man/ipa-client-install.1       |  5 ++++
 2 files changed, 36 insertions(+), 18 deletions(-)

diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index ccaab5536e83b4b6ac60b81132c3455c0af19ae1..6bd92b5074f32e04a90a09ddb49dc24eeaf5dc1d 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -91,6 +91,13 @@ def parse_options():
 
         parser.values.ca_cert_file = value
 
+    def validate_kinit_attempts_option(option, opt, value, parser):
+        if value < 1 or value > sys.maxint:
+            raise OptionValueError(
+                "%s option has invalid value %d" % (opt, value))
+
+        parser.values.kinit_attempts = value
+
     parser = IPAOptionParser(version=version.VERSION)
 
     basic_group = OptionGroup(parser, "basic options")
@@ -144,6 +151,11 @@ def parse_options():
                       help="do not modify the nsswitch.conf and PAM configuration")
     basic_group.add_option("-f", "--force", dest="force", action="store_true",
                       default=False, help="force setting of LDAP/Kerberos conf")
+    basic_group.add_option('--kinit-attempts', dest='kinit_attempts',
+                           action='callback', type='int', default=5,
+                           callback=validate_kinit_attempts_option,
+                           help=("number of attempts to obtain host TGT"
+                                 " (defaults to %default)."))
     basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
                       default=False, help="print debugging information")
     basic_group.add_option("-U", "--unattended", dest="unattended",
@@ -2351,6 +2363,7 @@ def install(options, env, fstore, statestore):
             root_logger.debug(
                 "will use principal provided as option: %s", options.principal)
 
+    host_principal = 'host/%s@%s' % (hostname, cli_realm)
     if not options.on_master:
         nolog = tuple()
         # First test out the kerberos configuration
@@ -2421,17 +2434,15 @@ def install(options, env, fstore, statestore):
             elif options.keytab:
                 join_args.append("-f")
                 if os.path.exists(options.keytab):
-                    (stderr, stdout, returncode) = run(
-                        [paths.KINIT,'-k', '-t', options.keytab,
-                            'host/%s@%s' % (hostname, cli_realm)],
-                        env=env,
-                        raiseonerr=False)
-
-                    if returncode != 0:
+                    try:
+                        ipautil.kinit_hostprincipal(
+                            options.keytab, ccache_name, host_principal,
+                            attempts=options.kinit_attempts)
+                    except StandardError, err:
                         print_port_conf_info()
                         root_logger.error("Kerberos authentication failed "
-                                          "using keytab: %s", options.keytab)
-                        root_logger.info("%s", stdout)
+                                          "using keytab '%s': "
+                                          "%s" % (options.keytab, str(err)))
                         return CLIENT_INSTALL_ERROR
                 else:
                     root_logger.error("Keytab file could not be found: %s"
@@ -2503,10 +2514,12 @@ def install(options, env, fstore, statestore):
             # Once we have the TGT, it's usable on any server.
             env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE
             try:
-                run([paths.KINIT, '-k', '-t', paths.KRB5_KEYTAB,
-                        'host/%s@%s' % (hostname, cli_realm)], env=env)
-            except CalledProcessError, e:
-                root_logger.error("Failed to obtain host TGT.")
+                ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, CCACHE_FILE,
+                                            host_principal,
+                                            attempts=options.kinit_attempts)
+            except StandardError, err:
+                print_port_conf_info()
+                root_logger.error("Failed to obtain host TGT: %s" % str(err))
                 # failure to get ticket makes it impossible to login and bind
                 # from sssd to LDAP, abort installation and rollback changes
                 return CLIENT_INSTALL_ERROR
@@ -2543,16 +2556,16 @@ def install(options, env, fstore, statestore):
             return CLIENT_INSTALL_ERROR
         root_logger.info("Configured /etc/sssd/sssd.conf")
 
-    host_principal = 'host/%s@%s' % (hostname, cli_realm)
     if options.on_master:
         # If on master assume kerberos is already configured properly.
         # Get the host TGT.
         os.environ['KRB5CCNAME'] = CCACHE_FILE
         try:
-            run([paths.KINIT, '-k', '-t', paths.KRB5_KEYTAB,
-                    host_principal])
-        except CalledProcessError, e:
-            root_logger.error("Failed to obtain host TGT.")
+            ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, CCACHE_FILE,
+                                        host_principal,
+                                        attempts=options.kinit_attempts)
+        except StandardError, err:
+            root_logger.error("Failed to obtain host TGT: %s" % str(err))
             return CLIENT_INSTALL_ERROR
     else:
         # Configure krb5.conf
diff --git a/ipa-client/man/ipa-client-install.1 b/ipa-client/man/ipa-client-install.1
index 726a6c133132dd2e3ba2fde43d8a2ec0549bfcef..56ed899a25e626b8ae61714f77f3588059fa86f9 100644
--- a/ipa-client/man/ipa-client-install.1
+++ b/ipa-client/man/ipa-client-install.1
@@ -152,6 +152,11 @@ Do not use Authconfig to modify the nsswitch.conf and PAM configuration.
 \fB\-f\fR, \fB\-\-force\fR
 Force the settings even if errors occur
 .TP
+\fB\-\-kinit\-attempts\fR=\fIKINIT_ATTEMPTS\fR
+Number of unsuccessful attempts to obtain host TGT that will be performed
+before aborting client installation. \fIKINIT_ATTEMPTS\fR should be a number
+greater than zero. By default 5 attempts to get TGT are performed.
+.TP
 \fB\-d\fR, \fB\-\-debug\fR
 Print debugging information to stdout
 .TP
-- 
2.1.0

From 78785b0fae21f3fc0ed6ebabf18f8b718669b9dc Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 6 Mar 2015 12:43:38 +0100
Subject: [PATCH 3/5] ipa-client-automount: use updated
 ipautil.kinit_hostprincipal from PATCH 0015

the function is used to get host TGT using keytab

---
 ipa-client/ipa-install/ipa-client-automount | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/ipa-client/ipa-install/ipa-client-automount b/ipa-client/ipa-install/ipa-client-automount
index 7b9e701dead5f50a033a455eb62e30df78cc0249..5db4d1b77dffbb5d4549358500f4a11a49eebedb 100755
--- a/ipa-client/ipa-install/ipa-client-automount
+++ b/ipa-client/ipa-install/ipa-client-automount
@@ -426,9 +426,11 @@ def main():
     try:
         try:
             os.environ['KRB5CCNAME'] = ccache_name
-            ipautil.run([paths.KINIT, '-k', '-t', paths.KRB5_KEYTAB, 'host/%s@%s' % (api.env.host, api.env.realm)])
-        except ipautil.CalledProcessError, e:
-            sys.exit("Failed to obtain host TGT.")
+            host_princ = str('host/%s@%s' % (api.env.host, api.env.realm))
+            ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, ccache_name,
+                                        host_princ)
+        except StandardError, e:
+            sys.exit("Failed to obtain host TGT: %s" % e)
         # Now we have a TGT, connect to IPA
         try:
             api.Backend.rpcclient.connect()
-- 
2.1.0

From 0fc5386b4fd5a4b0c33a7167498b129064df4d0f Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 6 Mar 2015 12:45:17 +0100
Subject: [PATCH 4/5] rpcserver.py: use ipautil.kinit_hostprincipal to obtain
 host TGT using keytab

---
 ipaserver/rpcserver.py | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index d6bc955b9d9910a24eec5df1def579310eb54786..914fa6dd89ae9fae11ed4e40ad052deb8269569d 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -967,11 +967,10 @@ class login_password(Backend, KerberosSession, HTTP_Status):
         self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s',
                    armor_principal, keytab, armor_path)
 
-        (stdout, stderr, returncode) = ipautil.run(
-            [paths.KINIT, '-kt', keytab, armor_principal],
-            env={'KRB5CCNAME': armor_path}, raiseonerr=False)
-
-        if returncode != 0:
+        try:
+            ipautil.kinit_hostprincipal(paths.IPA_KEYTAB, armor_path,
+                                        armor_principal)
+        except StandardError:
             raise CCacheError()
 
         # Format the user as a kerberos principal
-- 
2.1.0

From 7b12fe99547b5e7640013c0059146346f710dff8 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Fri, 6 Mar 2015 12:45:47 +0100
Subject: [PATCH 5/5] updated existing calls to ipautil.kinit_hostprincipal to
 reflect changes in PATCH 0015

---
 daemons/dnssec/ipa-dnskeysync-replica               | 6 ++++--
 daemons/dnssec/ipa-dnskeysyncd                      | 3 ++-
 daemons/dnssec/ipa-ods-exporter                     | 3 ++-
 install/certmonger/dogtag-ipa-ca-renew-agent-submit | 3 ++-
 install/restart_scripts/renew_ca_cert               | 7 ++++---
 install/restart_scripts/renew_ra_cert               | 5 +++--
 ipa-client/ipaclient/ipa_certupdate.py              | 4 +++-
 7 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/daemons/dnssec/ipa-dnskeysync-replica b/daemons/dnssec/ipa-dnskeysync-replica
index d04f360e04ee018dcdd1ba9b2ca42b1844617af9..34d64b2a23a834cf2245d97e482e64f3f4b3f8ca 100755
--- a/daemons/dnssec/ipa-dnskeysync-replica
+++ b/daemons/dnssec/ipa-dnskeysync-replica
@@ -139,14 +139,16 @@ log.setLevel(level=logging.DEBUG)
 # Kerberos initialization
 PRINCIPAL = str('%s/%s' % (DAEMONNAME, ipalib.api.env.host))
 log.debug('Kerberos principal: %s', PRINCIPAL)
-ipautil.kinit_hostprincipal(paths.IPA_DNSKEYSYNCD_KEYTAB, WORKDIR, PRINCIPAL)
+ccache_filename = os.path.join(WORKDIR, 'ccache')
+ipautil.kinit_hostprincipal(paths.IPA_DNSKEYSYNCD_KEYTAB, ccache_filename,
+                            PRINCIPAL)
 log.debug('Got TGT')
 
 # LDAP initialization
 ldap = ipalib.api.Backend[ldap2]
 # fixme
 log.debug('Connecting to LDAP')
-ldap.connect(ccache="%s/ccache" % WORKDIR)
+ldap.connect(ccache=ccache_filename)
 log.debug('Connected')
 
 
diff --git a/daemons/dnssec/ipa-dnskeysyncd b/daemons/dnssec/ipa-dnskeysyncd
index 54a08a1e6307e89b3f52e78bddbe28cda8ac1345..a0aba89ab03875991692929254a00d36188e7fee 100755
--- a/daemons/dnssec/ipa-dnskeysyncd
+++ b/daemons/dnssec/ipa-dnskeysyncd
@@ -65,7 +65,8 @@ log = root_logger
 # Kerberos initialization
 PRINCIPAL = str('%s/%s' % (DAEMONNAME, api.env.host))
 log.debug('Kerberos principal: %s', PRINCIPAL)
-ipautil.kinit_hostprincipal(KEYTAB_FB, WORKDIR, PRINCIPAL)
+ipautil.kinit_hostprincipal(KEYTAB_FB, os.path.join(WORKDIR, 'ccache'),
+                            PRINCIPAL)
 
 # LDAP initialization
 basedn = DN(api.env.container_dns, api.env.basedn)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index dc1851d3a34bb09c1a87c86d101b11afe35e49fe..c16cb5e64bdd68dce885e35c3f81d83c12d90608 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -399,7 +399,8 @@ ipalib.api.finalize()
 # Kerberos initialization
 PRINCIPAL = str('%s/%s' % (DAEMONNAME, ipalib.api.env.host))
 log.debug('Kerberos principal: %s', PRINCIPAL)
-ipautil.kinit_hostprincipal(paths.IPA_ODS_EXPORTER_KEYTAB, WORKDIR, PRINCIPAL)
+ipautil.kinit_hostprincipal(paths.IPA_ODS_EXPORTER_KEYTAB,
+                            os.path.join(WORKDIR, 'ccache'), PRINCIPAL)
 log.debug('Got TGT')
 
 # LDAP initialization
diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
index 7b91fc61148912c77d0ae962b3847d73e8d0720e..bf38570acd8ef8c91102b86e7cb375a1dcbacbc6 100755
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
@@ -440,7 +440,8 @@ def main():
     certs.renewal_lock.acquire()
     try:
         principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-        ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir, principal)
+        ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB,
+                                    os.path.join(tmpdir, 'ccache'), principal)
 
         profile = os.environ.get('CERTMONGER_CA_PROFILE')
         if profile:
diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert
index c7bd5d74c5b4659b3ad66d630653ff6419868d99..24e22a54e5695765a1ca7c836b4c74338462df07 100644
--- a/install/restart_scripts/renew_ca_cert
+++ b/install/restart_scripts/renew_ca_cert
@@ -73,8 +73,9 @@ def _main():
     tmpdir = tempfile.mkdtemp(prefix="tmp-")
     try:
         principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-        ccache = ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir,
-                                             principal)
+        ccache_filename = '%s/ccache' % tmpdir
+        ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, ccache_filename,
+                                    principal)
 
         ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
         ca.update_cert_config(nickname, cert, configured_constants)
@@ -139,7 +140,7 @@ def _main():
             conn = None
             try:
                 conn = ldap2(shared_instance=False, ldap_uri=api.env.ldap_uri)
-                conn.connect(ccache=ccache)
+                conn.connect(ccache=ccache_filename)
             except Exception, e:
                 syslog.syslog(
                     syslog.LOG_ERR, "Failed to connect to LDAP: %s" % e)
diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert
index 7dae3562380e919b2cc5f53825820291fc93fdc5..eaac5a6034ce62bbeafd1c415a59fdca2ff348fe 100644
--- a/install/restart_scripts/renew_ra_cert
+++ b/install/restart_scripts/renew_ra_cert
@@ -42,8 +42,9 @@ def _main():
     tmpdir = tempfile.mkdtemp(prefix="tmp-")
     try:
         principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-        ccache = ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir,
-                                             principal)
+        ccache_filename = '%s/ccache' % tmpdir
+        ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, ccache_filename,
+                                    principal)
 
         ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
         if ca.is_renewal_master():
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 031a34c3a54a02d43978eedcb794678a1550702b..5295266444e82b4efcd698da886894366165eb1a 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -57,7 +57,9 @@ class CertUpdate(admintool.AdminTool):
         tmpdir = tempfile.mkdtemp(prefix="tmp-")
         try:
             principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-            ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir, principal)
+            ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB,
+                                        os.path.join(tmpdir, 'ccache'),
+                                        principal)
 
             api.Backend.rpcclient.connect()
             try:
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to