From d6120756dc57ae2d279675ff337ecd4b81a62b40 Mon Sep 17 00:00:00 2001 From: Andrew Walker Date: Tue, 20 Aug 2024 12:21:11 -0600 Subject: [PATCH] Fix LDAP status on failover This fix addresses a few minor issues with directory services state recovery. 1. Rather than simply failing directoryservices.setup if unhealthy, we should take recovery steps 2. If for some reason SSSD fails to start triggering a health check failure we should go through etc.generate steps to make sure we have all required files written before trying to start it again. 3. Always generate the nsswitch.conf after setting up directory services. This avoids possibilty of having SSSD / winbind running without them being present in nsswitch.conf on failover. (cherry picked from commit d9503e85f5f92498e017b728e513f0c623c3fea8) --- .../middlewared/plugins/directoryservices.py | 16 +++++++------- .../directoryservices_/ipa_health_mixin.py | 5 +---- .../directoryservices_/ldap_health_mixin.py | 5 +---- src/middlewared/middlewared/plugins/smb.py | 21 ++++++------------- 4 files changed, 17 insertions(+), 30 deletions(-) diff --git a/src/middlewared/middlewared/plugins/directoryservices.py b/src/middlewared/middlewared/plugins/directoryservices.py index 91472b515c8a6..12576ca7e75cf 100644 --- a/src/middlewared/middlewared/plugins/directoryservices.py +++ b/src/middlewared/middlewared/plugins/directoryservices.py @@ -35,7 +35,8 @@ class Config: @returns(Dict( 'directoryservices_status', Str('type', enum=[x.value for x in DSType], null=True), - Str('status', enum=[status.name for status in DSStatus], null=True) + Str('status', enum=[status.name for status in DSStatus], null=True), + Str('status_msg', null=True) )) def status(self): """ @@ -47,9 +48,7 @@ def status(self): except Exception: pass - status = DSHealthObj.dump() - status.pop('status_msg') - return status + return DSHealthObj.dump() @no_authz_required @accepts() @@ -218,12 +217,15 @@ def setup(self, job): job.set_progress(100, f'{failover_status}: skipping directory service setup due to failover status') return - self.middleware.call_sync('service.restart', 'idmap') - - self.middleware.call_sync('directoryservices.health.check') + # Recover is called here because it short-circuits if health check + # shows we're healthy. If we can't recover due to things being irreparably + # broken then this will raise an exception. + self.middleware.call_sync('directoryservices.health.recover') if DSHealthObj.dstype is None: return + # nsswitch.conf needs to be updated + self.middleware.call_sync('etc.generate', 'nss') job.set_progress(10, 'Refreshing cache'), cache_refresh = self.middleware.call_sync('directoryservices.cache.refresh') cache_refresh.wait_sync() diff --git a/src/middlewared/middlewared/plugins/directoryservices_/ipa_health_mixin.py b/src/middlewared/middlewared/plugins/directoryservices_/ipa_health_mixin.py index 168b4ea1915d3..a89c020d3b04d 100644 --- a/src/middlewared/middlewared/plugins/directoryservices_/ipa_health_mixin.py +++ b/src/middlewared/middlewared/plugins/directoryservices_/ipa_health_mixin.py @@ -29,11 +29,8 @@ def _recover_ipa(self, error: IPAHealthError) -> None: self._recover_ipa_config() case IPAHealthCheckFailReason.IPA_NO_CACERT | IPAHealthCheckFailReason.IPA_CACERT_PERM: self._recover_ipa_config() - case IPAHealthCheckFailReason.LDAP_BIND_FAILED: + case IPAHealthCheckFailReason.LDAP_BIND_FAILED | IPAHealthCheckFailReason.SSSD_STOPPED: self._recover_ldap_config() - case IPAHealthCheckFailReason.SSSD_STOPPED: - # pick up with sssd restart below - pass case _: # not recoverable raise error from None diff --git a/src/middlewared/middlewared/plugins/directoryservices_/ldap_health_mixin.py b/src/middlewared/middlewared/plugins/directoryservices_/ldap_health_mixin.py index 39ccaccfb15be..dff17b9aa1dec 100644 --- a/src/middlewared/middlewared/plugins/directoryservices_/ldap_health_mixin.py +++ b/src/middlewared/middlewared/plugins/directoryservices_/ldap_health_mixin.py @@ -15,11 +15,8 @@ def _recover_ldap(self, error: LDAPHealthError) -> None: our health check. """ match error.reason: - case LDAPHealthCheckFailReason.LDAP_BIND_FAILED: + case LDAPHealthCheckFailReason.LDAP_BIND_FAILED | LDAPHealthCheckFailReason.SSSD_STOPPED: self._recover_ldap_config() - case LDAPHealthCheckFailReason.SSSD_STOPPED: - # pick up with sssd restart below - pass case _: # not recoverable raise error from None diff --git a/src/middlewared/middlewared/plugins/smb.py b/src/middlewared/middlewared/plugins/smb.py index 747e2a4938ea3..f3daffc12dfc6 100644 --- a/src/middlewared/middlewared/plugins/smb.py +++ b/src/middlewared/middlewared/plugins/smb.py @@ -382,21 +382,12 @@ async def configure(self, job, create_paths=True): job.set_progress(30, 'Setting up server SID.') await self.middleware.call('smb.set_system_sid') - """ - If the ldap passdb backend is being used, then the remote LDAP server - will provide the SMB users and groups. We skip these steps to avoid having - samba potentially try to write our local users and groups to the remote - LDAP server. - - """ - passdb_backend = await self.middleware.call('smb.getparm', 'passdb backend', 'global') - if passdb_backend.startswith("tdbsam"): - job.set_progress(40, 'Synchronizing passdb and groupmap.') - await self.middleware.call('etc.generate', 'user') - pdb_job = await self.middleware.call("smb.synchronize_passdb", True) - grp_job = await self.middleware.call("smb.synchronize_group_mappings", True) - await pdb_job.wait() - await grp_job.wait() + job.set_progress(40, 'Synchronizing passdb and groupmap.') + await self.middleware.call('etc.generate', 'user') + pdb_job = await self.middleware.call("smb.synchronize_passdb", True) + grp_job = await self.middleware.call("smb.synchronize_group_mappings", True) + await pdb_job.wait() + await grp_job.wait() """ The following steps ensure that we cleanly import our SMB shares