From 7279ef1d0f28dae9f3203362ca9e2245e56e111f Mon Sep 17 00:00:00 2001 From: Stanislav Laznicka Date: Nov 11 2016 11:13:56 +0000 Subject: Moved update of DNA plugin among update plugins To make the code more general, moved the update_dna_shared_config among other update plugins. Bugfix: DNA shared config connection protocol was compared to a method string which would result in a try to always update it even if there was no need to. https://fedorahosted.org/389/ticket/48373 causes that two shared DNA config entries are created instead of one. https://fedorahosted.org/freeipa/ticket/6392 Reviewed-By: Martin Babinsky Reviewed-By: Jan Cholasta --- diff --git a/install/share/dna.ldif b/install/share/dna.ldif index 86be44c..f4bff36 100644 --- a/install/share/dna.ldif +++ b/install/share/dna.ldif @@ -14,4 +14,11 @@ dnaFilter: (|(objectClass=posixAccount)(objectClass=posixGroup)(objectClass=ipaI dnaScope: $SUFFIX dnaThreshold: 500 dnaSharedCfgDN: cn=posix-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX +dnaExcludeScope: cn=provisioning,$SUFFIX + +# Enable the DNA plugin +dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config +changetype: modify +replace: nsslapd-pluginEnabled +nsslapd-pluginEnabled: on diff --git a/install/updates/20-dna.update b/install/updates/20-dna.update index 75fae7b..d0496da 100644 --- a/install/updates/20-dna.update +++ b/install/updates/20-dna.update @@ -1,12 +1,4 @@ -# Enable the DNA plugin -dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config -only:nsslapd-pluginEnabled: on - -# Change the magic value to -1 -dn: cn=Posix IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config -only:dnaMagicRegen: -1 -add: dnaExcludeScope: cn=provisioning,$SUFFIX - +# Config winsync plugin according to DNA dn: cn=ipa-winsync,cn=plugins,cn=config remove:ipaWinSyncUserAttr: uidNumber 999 remove:ipaWinSyncUserAttr: gidNumber 999 diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update index 860cc41..7c672e4 100644 --- a/install/updates/90-post_upgrade_plugins.update +++ b/install/updates/90-post_upgrade_plugins.update @@ -28,3 +28,4 @@ plugin: update_idrange_baserid plugin: update_passync_privilege_update plugin: update_dnsserver_configuration_into_ldap plugin: update_ldap_server_list +plugin: update_dna_shared_config diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index 49289d4..a7d1b64 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -1282,105 +1282,3 @@ class DsInstance(service.Service): # check for open secure port 636 from now on self.open_ports.append(636) - - def update_dna_shared_config(self, method="SASL/GSSAPI", protocol="LDAP"): - - dna_bind_method = "dnaRemoteBindMethod" - dna_conn_protocol = "dnaRemoteConnProtocol" - dna_plugin = DN(('cn', 'Distributed Numeric Assignment Plugin'), - ('cn', 'plugins'), - ('cn', 'config')) - dna_config_base = DN(('cn', 'posix IDs'), dna_plugin) - - conn = self.admin_conn - - # Check the plugin is enabled else it is useless to update - # the shared entry - try: - entry = conn.get_entry(dna_plugin) - if entry.single_value.get('nsslapd-pluginenabled') == 'off': - return - except errors.NotFound: - root_logger.error("Could not find DNA plugin entry: %s" % - dna_config_base) - return - - try: - entry = conn.get_entry(dna_config_base) - except errors.NotFound: - root_logger.error("Could not find DNA config entry: %s" % - dna_config_base) - return - - sharedcfgdn = entry.single_value.get("dnaSharedCfgDN") - if sharedcfgdn is not None: - sharedcfgdn = DN(sharedcfgdn) - else: - root_logger.error( - "Could not find DNA shared config DN in entry: %s" % - dna_config_base) - return - - # - # Update the shared config entry related to that host - # - # If the shared config entry already exists (like upgrade) - # the update occurs immediately without sleep. - # - # If the shared config entry does not exist (fresh install) - # DS server waits for 30s after its startup to create it. - # Startup likely occurred few sec before this function is - # called so this loop will wait for 30s max. - # - # In case the server is not able to create the entry - # The loop gives a grace period of 60s before logging - # the failure to update the shared config entry and return - # - max_wait = 30 - for _i in range(0, max_wait + 1): - try: - entries = conn.get_entries( - sharedcfgdn, scope=ldap.SCOPE_ONELEVEL, - filter='dnaHostname=%s' % self.fqdn - ) - break - except errors.NotFound: - root_logger.debug( - "Unable to find DNA shared config entry for " - "dnaHostname=%s (under %s) so far. Retry in 2 sec." % - (self.fqdn, sharedcfgdn) - ) - time.sleep(2) - else: - root_logger.error( - "Could not get dnaHostname entries in {} seconds".format( - max_wait * 2) - ) - return - - # If there are several entries, all of them will be updated - # just log a debug msg. This is likely the result of #5510 - if len(entries) != 1: - root_logger.debug( - "%d entries dnaHostname=%s under %s. One expected" % - (len(entries), self.fqdn, sharedcfgdn) - ) - - # time to set the bind method and the protocol in the - # shared config entries - for entry in entries: - mod = [] - if entry.single_value.get(dna_bind_method) != method: - mod.append((ldap.MOD_REPLACE, dna_bind_method, method)) - - if entry.single_value.get(dna_conn_protocol) != method: - mod.append((ldap.MOD_REPLACE, dna_conn_protocol, protocol)) - - if mod: - try: - conn.modify_s(entry.dn, mod) - except Exception as e: - root_logger.error( - "Failed to set SASL/GSSAPI bind method/protocol " - "in entry {}: {}".format(entry, e) - ) diff --git a/ipaserver/install/plugins/update_dna_shared_config.py b/ipaserver/install/plugins/update_dna_shared_config.py new file mode 100644 index 0000000..21ed9c4 --- /dev/null +++ b/ipaserver/install/plugins/update_dna_shared_config.py @@ -0,0 +1,125 @@ +# +# Copyright (C) 2016 FreeIPA Contributors see COPYING for license +# + +import time +import ldap + +from ipalib.plugable import Registry +from ipalib import errors +from ipalib import Updater +from ipapython.dn import DN + +register = Registry() + + +@register() +class update_dna_shared_config(Updater): + def execute(self, **options): + method = options.get('method', "SASL/GSSAPI") + protocol = options.get('protocol', "LDAP") + + dna_bind_method = "dnaRemoteBindMethod" + dna_conn_protocol = "dnaRemoteConnProtocol" + dna_plugin = DN(('cn', 'Distributed Numeric Assignment Plugin'), + ('cn', 'plugins'), + ('cn', 'config')) + dna_config_base = DN(('cn', 'posix IDs'), dna_plugin) + + conn = self.api.Backend.ldap2 + + # Check the plugin is enabled else it is useless to update + # the shared entry + try: + entry = conn.get_entry(dna_plugin) + if entry.single_value.get('nsslapd-pluginenabled') == 'off': + return False, () + except errors.NotFound: + self.log.error("Could not find DNA plugin entry: %s" % + dna_config_base) + return False, () + + try: + entry = conn.get_entry(dna_config_base) + except errors.NotFound: + self.log.error("Could not find DNA config entry: %s" % + dna_config_base) + return False, () + + sharedcfgdn = entry.single_value.get("dnaSharedCfgDN") + if sharedcfgdn is not None: + sharedcfgdn = DN(sharedcfgdn) + else: + self.log.error( + "Could not find DNA shared config DN in entry: %s" % + dna_config_base) + return False, () + + # + # Update the shared config entry related to that host + # + # If the shared config entry already exists (like upgrade) + # the update occurs immediately without sleep. + # + # If the shared config entry does not exist (fresh install) + # DS server waits for 30s after its startup to create it. + # Startup likely occurred few sec before this function is + # called so this loop will wait for 30s max. + # + # In case the server is not able to create the entry + # The loop gives a grace period of 60s before logging + # the failure to update the shared config entry and return + # + max_wait = 30 + fqdn = self.api.env.host + for _i in range(0, max_wait + 1): + try: + entries = conn.get_entries( + sharedcfgdn, scope=ldap.SCOPE_ONELEVEL, + filter='dnaHostname=%s' % fqdn + ) + break + except errors.NotFound: + self.log.debug( + "Unable to find DNA shared config entry for " + "dnaHostname=%s (under %s) so far. Retry in 2 sec." % + (fqdn, sharedcfgdn) + ) + time.sleep(2) + else: + self.log.error( + "Could not get dnaHostname entries in {} seconds".format( + max_wait * 2) + ) + return False, () + + # If there are several entries, all of them will be updated + # just log a debug msg. This is likely the result of #5510 + if len(entries) != 1: + self.log.debug( + "%d entries dnaHostname=%s under %s. One expected" % + (len(entries), fqdn, sharedcfgdn) + ) + + # time to set the bind method and the protocol in the + # shared config entries + for entry in entries: + update = False + if entry.single_value.get(dna_bind_method) != method: + entry[dna_bind_method] = method + update = True + + if entry.single_value.get(dna_conn_protocol) != protocol: + entry[dna_conn_protocol] = protocol + update = True + + if update: + try: + conn.update_entry(entry) + except Exception as e: + self.log.error( + "Failed to set SASL/GSSAPI bind method/protocol " + "in entry {}: {}".format(entry, e) + ) + # no restart, no update + return False, () diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py index 34d6793..b5826f3 100644 --- a/ipaserver/install/server/install.py +++ b/ipaserver/install/server/install.py @@ -869,10 +869,6 @@ def install(installer): service.print_msg("Restarting the web server") http.restart() - # update DNA shared config entry is done as far as possible - # from restart to avoid waiting for its creation - ds.update_dna_shared_config() - # Set the admin user kerberos password ds.change_admin_password(admin_password) diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py index ba9b0f0..ca889fb 100644 --- a/ipaserver/install/server/replicainstall.py +++ b/ipaserver/install/server/replicainstall.py @@ -897,10 +897,6 @@ def install(installer): print("Configuration of client side components failed!") raise RuntimeError("Failed to configure the client") - # update DNA shared config entry is done as far as possible - # from restart to avoid waiting for its creation - ds.update_dna_shared_config() - # Everything installed properly, activate ipa service. services.knownservices.ipa.enable() @@ -1527,10 +1523,6 @@ def promote(installer): config.dirman_password, kra_cert_bundle=ca_data) - # update DNA shared config entry is done as far as possible - # from restart to avoid waiting for its creation - ds.update_dna_shared_config() - custodia.import_dm_password(config.master_host_name) promote_sssd(config.host_name) diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index bea1276..5f61015 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -1615,7 +1615,6 @@ def upgrade_configuration(): ds.principal = "ldap/%s@%s" % (ds.fqdn, ds.realm) ds_enable_sidgen_extdom_plugins(ds) - ds.update_dna_shared_config() # Now 389-ds is available, run the remaining http tasks if not http.is_kdcproxy_configured():