[Freeipa-devel] [PATCH] 0012 Modify existing SSSD configuration instead of dropping it

Alexander Bokovoy abokovoy at redhat.com
Thu Oct 13 12:38:12 UTC 2011


On Wed, 12 Oct 2011, Rob Crittenden wrote:
> Well, in the "generate new file" option I think the output is a bit
> misleading.
> 
> +        print "New SSSD config will be generated. The old one is
> backed up and can be restored during uninstall"
> 
> There could have been no existing sssd.conf, right?
> 
> +        logging.error("Failed to parse SSSD configuration and will
> generate new one")
> 
> This could imply that an error occurred when in fact there just was
> no sssd.conf to import.
> 
> Otherwise the approach looks good.
New version is attached. Works fine for me -- both in case 
uninstall-after-upgrade (means no installation code was run and 
nothing in state/file store) and normal install/uninstall. I also 
tried installing/uninstalling with missing sssd.conf and all went 
well:

1. Missing sssd.conf
---------------------
Enrolled in IPA realm IPA.LOCAL
Created /etc/ipa/default.conf
New SSSD config will be created.
Configured /etc/sssd/sssd.conf
Configured /etc/krb5.conf for IPA realm IPA.LOCAL
SSSD enabled
NTP enabled
Client configuration complete.
---------------------

2. Invalid sssd.conf
---------------------
Enrolled in IPA realm IPA.LOCAL
Created /etc/ipa/default.conf
Unable to parse existing SSSD config. As option --preserve-sssd was not specified, new config will override the old one.
The old /etc/sssd/sssd.conf is backed up and will be restored during uninstall.
root        : ERROR    Unable to parse existing SSSD config and --preserve-sssd was not specified: 
Configured /etc/sssd/sssd.conf
Configured /etc/krb5.conf for IPA realm IPA.LOCAL
SSSD enabled
NTP enabled
Client configuration complete.
---------------------

3. Existing proper sssd.conf
---------------------
Enrolled in IPA realm IPA.LOCAL
Created /etc/ipa/default.conf
Configured /etc/sssd/sssd.conf
Configured /etc/krb5.conf for IPA realm IPA.LOCAL
SSSD enabled
NTP enabled
Client configuration complete.
---------------------

The only visible difference is change of comment symbol from ';' to 
'#' for the existing correct sssd.conf, but that's how SSSDConfig 
works.
-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 51b6ac3c8f65f5686b87243dbeb9c33e3ac6ee58 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Wed, 12 Oct 2011 19:14:55 +0300
Subject: [PATCH] Refactor authconfig use in ipa-client-install

When certain features are being configured via authconfig, we need to remember
what was configured and what was the state before it so that during uninstall
we restore proper state of the services.

Mostly it affects sssd configuration with multiple domains but also pre-existing
LDAP and krb5 configurations.

This should fix following tickets:
https://fedorahosted.org/freeipa/ticket/1750
https://fedorahosted.org/freeipa/ticket/1769
---
 ipa-client/ipa-install/ipa-client-install |  110 +++++++++++++++++++++++-----
 ipapython/sysrestore.py                   |   13 ++++
 2 files changed, 103 insertions(+), 20 deletions(-)

diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index e1487fae05097af7f62a38f8fa635d863dc8b008..2080e78587468a04ff7da0345b7c5e441f154713 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -108,6 +108,9 @@ def parse_options():
     sssd_group.add_option("-S", "--no-sssd", dest="sssd",
                       action="store_false", default=True,
                       help="Do not configure the client to use SSSD for authentication")
+    sssd_group.add_option("--preserve-sssd", dest="preserve_sssd",
+                      action="store_true", default=False,
+                      help="Preserve old SSSD configuration if possible")
     parser.add_option_group(sssd_group)
 
     uninstall_group = OptionGroup(parser, "uninstall options")
@@ -177,22 +180,33 @@ def uninstall(options, env, quiet=False):
         print "Refer to ipa-server-install for uninstallation."
         return CLIENT_NOT_CONFIGURED
 
-    sssdconfig = SSSDConfig.SSSDConfig()
-    sssdconfig.import_config()
-    domains = sssdconfig.list_active_domains()
-
     hostname = None
-    for name in domains:
-        domain = sssdconfig.get_domain(name)
-        try:
-            provider = domain.get_option('id_provider')
-        except SSSDConfig.NoOptionError:
-            continue
-        if provider == "ipa":
+    was_sssd_configured = False
+    try:
+        sssdconfig = SSSDConfig.SSSDConfig()
+        sssdconfig.import_config()
+        domains = sssdconfig.list_active_domains()
+        if len(domains) > 1:
+            # There was more than IPA domain configured
+            was_sssd_configured = True
+        for name in domains:
+            domain = sssdconfig.get_domain(name)
             try:
-                hostname = domain.get_option('ipa_hostname')
+                provider = domain.get_option('id_provider')
             except SSSDConfig.NoOptionError:
                 continue
+            if provider == "ipa":
+                try:
+                    hostname = domain.get_option('ipa_hostname')
+                except SSSDConfig.NoOptionError:
+                    continue
+    except Exception, e:
+        # We were unable to read existing SSSD config. This might mean few things:
+        # - sssd wasn't installed
+        # - sssd was removed after install and before uninstall
+        # - there are no active domains
+        # in both cases we cannot continue with SSSD
+        pass
 
     if hostname is None:
         hostname = socket.getfqdn()
@@ -266,14 +280,31 @@ def uninstall(options, env, quiet=False):
             logging.debug("Failed to remove Kerberos service principals: %s" % str(e))
 
     emit_quiet(quiet, "Disabling client Kerberos and LDAP configurations")
+    was_sssd_installed = False
+    if fstore.has_files():
+        was_sssd_installed = fstore.has_file("/etc/sssd/sssd.conf")
     try:
         auth_config = ipaservices.authconfig()
-        auth_config.disable("ldap").\
-                    disable("krb5").\
-                    disable("sssd").\
-                    disable("sssdauth").\
-                    disable("mkhomedir").\
-                    add_option("update")
+        if statestore.has_state('authconfig'):
+            # disable only those configurations that we enabled during install
+            for conf in ('ldap', 'krb5', 'sssd', 'sssdauth', 'mkhomedir'):
+                cnf = statestore.restore_state('authconfig', conf)
+                if cnf:
+                    auth_config.disable(conf)
+        else:
+            # There was no authconfig status store
+            # It means the code was upgraded after original install
+            # Fall back to old logic
+            auth_config.disable("ldap").\
+                        disable("krb5")
+            if not(was_sssd_installed and was_sssd_configured):
+                # assume there was sssd.conf before install and there were more than one domain in it
+                # In such case restoring sssd.conf will require us to keep SSSD running
+                auth_config.disable("sssd").\
+                            disable("sssdauth")
+            auth_config.disable("mkhomedir")
+
+        auth_config.add_option("update")
         auth_config.execute()
     except Exception, e:
         emit_quiet(quiet, "Failed to remove krb5/LDAP configuration. " +str(e))
@@ -345,6 +376,13 @@ def uninstall(options, env, quiet=False):
            if restored:
                ipaservices.knownservices.ntpd.restart()
 
+    if was_sssd_installed and was_sssd_configured:
+        # SSSD was installed before our installation, config now is restored, restart it
+        emit_quiet(quiet, "The original configuration of SSSD included other domains than IPA-based one.")
+        emit_quiet(quiet, "Original configuration file is restored, restarting SSSD service.")
+        sssd = ipaservices.service('sssd')
+        sssd.restart()
+
     if not options.unattended:
         emit_quiet(quiet, "The original nsswitch.conf configuration has been restored.")
         emit_quiet(quiet, "You may need to restart services or reboot the machine.")
@@ -612,8 +650,35 @@ def configure_certmonger(fstore, subject_base, cli_realm, hostname, options):
             print "%s request for host certificate failed" % (cmonger.service_name)
 
 def configure_sssd_conf(fstore, cli_realm, cli_domain, cli_server, options):
-    sssdconfig = SSSDConfig.SSSDConfig()
-    sssdconfig.new_config()
+    try:
+        sssdconfig = SSSDConfig.SSSDConfig()
+        sssdconfig.import_config()
+    except Exception, e:
+        if os.path.exists("/etc/sssd/sssd.conf") and options.preserve_sssd:
+            # SSSD config is in place but we are unable to read it
+            # In addition, we are instructed to preserve it
+            # This all means we can't use it and have to bail out
+            print "SSSD config exists but cannot be parsed: %s" % (str(e))
+            print "Correct errors in /etc/sssd/sssd.conf and re-run installation"
+            logging.error("Failed to parse SSSD configuration and was instructed to preserve existing SSSD config: %s" % (str(e)))
+            return 1
+
+        # SSSD configuration does not exist or we are not asked to preserve it, create new one
+        # We do make new SSSDConfig instance because IPAChangeConf-derived classes have no
+        # means to reset their state and ParseError exception could come due to parsing
+        # error from older version which cannot be upgraded anymore, leaving sssdconfig
+        # instance practically unusable
+        # Note that we already backed up sssd.conf before going into this routine
+        if type(e) is IOError:
+            print "New SSSD config will be created."
+        else:
+            # It was not IOError so it must have been parsing error
+            print "Unable to parse existing SSSD config. As option --preserve-sssd was not specified, new config will override the old one."
+            print "The old /etc/sssd/sssd.conf is backed up and will be restored during uninstall."
+            logging.error("Unable to parse existing SSSD config and --preserve-sssd was not specified: %s" % (str(e)))
+        logging.info("New SSSD config will be created")
+        sssdconfig = SSSDConfig.SSSDConfig()
+        sssdconfig.new_config()
 
     domain = sssdconfig.new_domain(cli_domain)
     domain.add_provider('ipa', 'id')
@@ -1081,16 +1146,20 @@ def install(options, env, fstore, statestore):
     # Modify nsswitch/pam stack
     auth_config = ipaservices.authconfig()
     if options.sssd:
+        statestore.backup_state('authconfig', 'sssd', True)
+        statestore.backup_state('authconfig', 'sssdauth', True)
         auth_config.enable("sssd").\
                     enable("sssdauth")
         message = "SSSD enabled"
         conf = 'SSSD'
     else:
+        statestore.backup_state('authconfig', 'ldap', True)
         auth_config.enable("ldap").\
                     enable("forcelegacy")
         message = "LDAP enabled"
 
     if options.mkhomedir:
+        statestore.backup_state('authconfig', 'mkhomedir', True)
         auth_config.enable("mkhomedir")
 
     auth_config.add_option("update")
@@ -1100,6 +1169,7 @@ def install(options, env, fstore, statestore):
     if not options.sssd:
         #Modify pam to add pam_krb5 only when sssd is not in use
         auth_config.reset()
+        statestore.backup_state('authconfig', 'krb5', True)
         auth_config.enable("krb5").\
                     add_option("update").\
                     add_option("nostart")
diff --git a/ipapython/sysrestore.py b/ipapython/sysrestore.py
index 9b0e39fcb6b2b5035f1bd5012ac598309c83e9ae..e22b4d4fa617141c3546290d7f049f5037651ce1 100644
--- a/ipapython/sysrestore.py
+++ b/ipapython/sysrestore.py
@@ -130,6 +130,19 @@ class FileStore:
         self.files[filename] = string.join([str(stat.st_mode),str(stat.st_uid),str(stat.st_gid),path], ',')
         self.save()
 
+    def has_file(self, path):
+        """Checks whether file at @path was added to the file store
+
+        Returns #True if the file exists in the file store, #False otherwise
+        """
+        result = False
+        for (key, value) in self.files.items():
+            (mode,uid,gid,filepath) = string.split(value, ',', 3)
+            if (filepath == path):
+                result = True
+                break
+        return result
+
     def restore_file(self, path):
         """Restore the copy of a file at @path to its original
         location and delete the copy.
-- 
1.7.6.4



More information about the Freeipa-devel mailing list