From bc7a1dc5b0c4a72827dc810297c42a3c42096195 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 10 Sep 2025 10:23:57 +0200 Subject: [PATCH 1/2] uuid mismatch notification --- src-tauri/src/enterprise/periodic/config.rs | 97 ++++++++++++++++++++- src-tauri/src/events.rs | 2 + src/i18n/en/index.ts | 3 + src/i18n/i18n-types.ts | 9 ++ src/pages/client/ClientPage.tsx | 15 ++++ src/pages/client/types.ts | 1 + 6 files changed, 124 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/enterprise/periodic/config.rs b/src-tauri/src/enterprise/periodic/config.rs index 5cb32286..80ae30df 100644 --- a/src-tauri/src/enterprise/periodic/config.rs +++ b/src-tauri/src/enterprise/periodic/config.rs @@ -206,6 +206,8 @@ pub async fn poll_instance( return Ok(()); } + check_uuid_mismatch(&response, instance, handle)?; + debug!( "Config for instance {}({}) changed", instance.name, instance.id @@ -269,11 +271,100 @@ fn build_request(instance: &Instance) -> Result }) } +#[derive(PartialEq, Eq, Hash)] +enum NotificationType { + VersionMismatch, + UuidMismatch, +} + /// Tracks instance IDs that for which we already sent notification about version mismatches /// to prevent duplicate notifications in the app's lifetime. -static NOTIFIED_INSTANCES: LazyLock>> = +static NOTIFIED_INSTANCES: LazyLock>> = LazyLock::new(|| Mutex::new(HashSet::new())); +#[derive(Clone, Serialize)] +struct UuidMismatchPayload { + instance_name: String, +} + +fn check_uuid_mismatch( + response: &InstanceInfoResponse, + instance: &Instance, + handle: &AppHandle, +) -> Result<(), Error> { + debug!( + "Checking UUID mismatch for instance {}({})", + instance.name, instance.id + ); + + let mut notified_instances = NOTIFIED_INSTANCES.lock().unwrap(); + if notified_instances.contains(&(instance.id, NotificationType::UuidMismatch)) { + debug!( + "Instance {}({}) already notified about UUID mismatch, skipping", + instance.name, instance.id + ); + return Ok(()); + } + + debug!( + "Instance {}({}) local UUID: {}, checking against response...", + instance.name, instance.id, instance.uuid + ); + + if let Some(device_config) = &response.device_config { + debug!( + "Found device_config for instance {}({})", + instance.name, instance.id + ); + + if let Some(info) = &device_config.instance { + debug!( + "Found instance info in device_config for instance {}({}), core UUID: {}", + instance.name, instance.id, info.id + ); + + if info.id != instance.uuid { + warn!( + "Instance {}({}) has mismatching UUIDs: local {}, remote {}", + instance.name, instance.id, instance.uuid, info.id + ); + + if let Err(err) = handle.emit( + EventKey::UuidMismatch.into(), + UuidMismatchPayload { + instance_name: instance.name.clone(), + }, + ) { + error!("Failed to emit UUID mismatch event to the frontend: {err}"); + } else { + debug!( + "Successfully emitted UUID mismatch event for instance {}({})", + instance.name, instance.id + ); + notified_instances.insert((instance.id, NotificationType::UuidMismatch)); + } + } else { + debug!( + "UUIDs match for instance {}({}): {}", + instance.name, instance.id, instance.uuid + ); + } + } else { + debug!( + "No instance info found in device_config for instance {}({})", + instance.name, instance.id + ); + } + } else { + debug!( + "No device_config found in response for instance {}({})", + instance.name, instance.id + ); + } + + Ok(()) +} + const CORE_VERSION_HEADER: &str = "defguard-core-version"; const PROXY_VERSION_HEADER: &str = "defguard-component-version"; @@ -295,7 +386,7 @@ fn check_min_version( handle: &AppHandle, ) -> Result<(), Error> { let mut notified_instances = NOTIFIED_INSTANCES.lock().unwrap(); - if notified_instances.contains(&instance.id) { + if notified_instances.contains(&(instance.id, NotificationType::VersionMismatch)) { debug!( "Instance {}({}) already notified about version mismatch, skipping", instance.name, instance.id @@ -391,7 +482,7 @@ fn check_min_version( if let Err(err) = handle.emit(EventKey::VersionMismatch.into(), payload) { error!("Failed to emit version mismatch event to the frontend: {err}"); } else { - notified_instances.insert(instance.id); + notified_instances.insert((instance.id, NotificationType::VersionMismatch)); } } diff --git a/src-tauri/src/events.rs b/src-tauri/src/events.rs index e628aade..68be593b 100644 --- a/src-tauri/src/events.rs +++ b/src-tauri/src/events.rs @@ -18,6 +18,7 @@ pub enum EventKey { AddInstance, MfaTrigger, VersionMismatch, + UuidMismatch, } impl From for &'static str { @@ -34,6 +35,7 @@ impl From for &'static str { EventKey::AddInstance => "add-instance", EventKey::MfaTrigger => "mfa-trigger", EventKey::VersionMismatch => "version-mismatch", + EventKey::UuidMismatch => "uuid-mismatch", } } } diff --git a/src/i18n/en/index.ts b/src/i18n/en/index.ts index 31a0c1bf..2dca9b4f 100644 --- a/src/i18n/en/index.ts +++ b/src/i18n/en/index.ts @@ -64,6 +64,9 @@ const en = { 'Your Defguard instance "{instance_name: string}" version is not supported by your Defguard Client version. \ Defguard Core version: {core_version: string} (required: {core_required_version: string}), Defguard Proxy version: {proxy_version: string} (required: {proxy_required_version: string}). \ Please contact your administrator.', + uuidMismatch: + 'The identifier (UUID) of the remote Defguard instance "{instance_name: string}" does not match the one stored locally. \ + Because of this, some features may not work correctly. To resolve this issue, remove the instance and add it again, or contact your administrator.', }, }, components: { diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 1ee57158..088f2ae0 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -193,6 +193,11 @@ type RootTranslation = { * @param {string} proxy_version */ versionMismatch: RequiredParams<'core_required_version' | 'core_version' | 'instance_name' | 'proxy_required_version' | 'proxy_version'> + /** + * T​h​e​ ​i​d​e​n​t​i​f​i​e​r​ ​(​U​U​I​D​)​ ​o​f​ ​t​h​e​ ​r​e​m​o​t​e​ ​D​e​f​g​u​a​r​d​ ​i​n​s​t​a​n​c​e​ ​"​{​i​n​s​t​a​n​c​e​_​n​a​m​e​}​"​ ​d​o​e​s​ ​n​o​t​ ​m​a​t​c​h​ ​t​h​e​ ​o​n​e​ ​s​t​o​r​e​d​ ​l​o​c​a​l​l​y​.​ ​ ​ ​ ​ ​ ​ ​ ​ ​B​e​c​a​u​s​e​ ​o​f​ ​t​h​i​s​,​ ​s​o​m​e​ ​f​e​a​t​u​r​e​s​ ​m​a​y​ ​n​o​t​ ​w​o​r​k​ ​c​o​r​r​e​c​t​l​y​.​ ​T​o​ ​r​e​s​o​l​v​e​ ​t​h​i​s​ ​i​s​s​u​e​,​ ​r​e​m​o​v​e​ ​t​h​e​ ​i​n​s​t​a​n​c​e​ ​a​n​d​ ​a​d​d​ ​i​t​ ​a​g​a​i​n​,​ ​o​r​ ​c​o​n​t​a​c​t​ ​y​o​u​r​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​. + * @param {string} instance_name + */ + uuidMismatch: RequiredParams<'instance_name'> } } components: { @@ -1884,6 +1889,10 @@ export type TranslationFunctions = { * Your Defguard instance "{instance_name}" version is not supported by your Defguard Client version. Defguard Core version: {core_version} (required: {core_required_version}), Defguard Proxy version: {proxy_version} (required: {proxy_required_version}). Please contact your administrator. */ versionMismatch: (arg: { core_required_version: string, core_version: string, instance_name: string, proxy_required_version: string, proxy_version: string }) => LocalizedString + /** + * The identifier (UUID) of the remote Defguard instance "{instance_name}" does not match the one stored locally. Because of this, some features may not work correctly. To resolve this issue, remove the instance and add it again, or contact your administrator. + */ + uuidMismatch: (arg: { instance_name: string }) => LocalizedString } } components: { diff --git a/src/pages/client/ClientPage.tsx b/src/pages/client/ClientPage.tsx index c6a9c76f..2bbdc31c 100644 --- a/src/pages/client/ClientPage.tsx +++ b/src/pages/client/ClientPage.tsx @@ -110,6 +110,20 @@ export const ClientPage = () => { ); }); + const uuidMismatch = listen<{ + instance_name: string; + local_uuid: string; + core_uuid: string; + }>(TauriEventKey.UUID_MISMATCH, (data) => { + const payload = data.payload; + toaster.error( + LL.common.messages.uuidMismatch({ + instance_name: payload.instance_name, + }), + { lifetime: -1 }, + ); + }); + const locationUpdate = listen(TauriEventKey.LOCATION_UPDATE, () => { const invalidate = [clientQueryKeys.getLocations, clientQueryKeys.getTunnels]; invalidate.forEach((key) => { @@ -180,6 +194,7 @@ export const ClientPage = () => { appConfigChanged.then((cleanup) => cleanup()); mfaTrigger.then((cleanup) => cleanup()); verionMismatch.then((cleanup) => cleanup()); + uuidMismatch.then((cleanup) => cleanup()); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/src/pages/client/types.ts b/src/pages/client/types.ts index 0d866443..9600fda7 100644 --- a/src/pages/client/types.ts +++ b/src/pages/client/types.ts @@ -114,4 +114,5 @@ export enum TauriEventKey { APPLICATION_CONFIG_CHANGED = 'application-config-changed', MFA_TRIGGER = 'mfa-trigger', VERSION_MISMATCH = 'version-mismatch', + UUID_MISMATCH = 'uuid-mismatch', } From a312bd33c3a8ec1f58d18a92aac5518fbca82903 Mon Sep 17 00:00:00 2001 From: Aleksander <170264518+t-aleksander@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:33:29 +0200 Subject: [PATCH 2/2] prevent update on uuid mismatch --- src-tauri/src/enterprise/periodic/config.rs | 48 ++++++++++++--------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/src-tauri/src/enterprise/periodic/config.rs b/src-tauri/src/enterprise/periodic/config.rs index 80ae30df..a4436921 100644 --- a/src-tauri/src/enterprise/periodic/config.rs +++ b/src-tauri/src/enterprise/periodic/config.rs @@ -206,7 +206,13 @@ pub async fn poll_instance( return Ok(()); } - check_uuid_mismatch(&response, instance, handle)?; + if check_uuid_mismatch(&response, instance, handle)? { + warn!("Instance {}({}) has UUID mismatch, it's config won't be automatically updated. Remove \ + the instance and add it again, or contact your administrator.", + instance.name, instance.id + ); + return Ok(()); + } debug!( "Config for instance {}({}) changed", @@ -291,20 +297,13 @@ fn check_uuid_mismatch( response: &InstanceInfoResponse, instance: &Instance, handle: &AppHandle, -) -> Result<(), Error> { +) -> Result { debug!( "Checking UUID mismatch for instance {}({})", instance.name, instance.id ); let mut notified_instances = NOTIFIED_INSTANCES.lock().unwrap(); - if notified_instances.contains(&(instance.id, NotificationType::UuidMismatch)) { - debug!( - "Instance {}({}) already notified about UUID mismatch, skipping", - instance.name, instance.id - ); - return Ok(()); - } debug!( "Instance {}({}) local UUID: {}, checking against response...", @@ -324,25 +323,34 @@ fn check_uuid_mismatch( ); if info.id != instance.uuid { - warn!( + error!( "Instance {}({}) has mismatching UUIDs: local {}, remote {}", instance.name, instance.id, instance.uuid, info.id ); - if let Err(err) = handle.emit( - EventKey::UuidMismatch.into(), - UuidMismatchPayload { - instance_name: instance.name.clone(), - }, - ) { - error!("Failed to emit UUID mismatch event to the frontend: {err}"); + if !notified_instances.contains(&(instance.id, NotificationType::UuidMismatch)) { + if let Err(err) = handle.emit( + EventKey::UuidMismatch.into(), + UuidMismatchPayload { + instance_name: instance.name.clone(), + }, + ) { + error!("Failed to emit UUID mismatch event to the frontend: {err}"); + } else { + debug!( + "Successfully emitted UUID mismatch event for instance {}({})", + instance.name, instance.id + ); + notified_instances.insert((instance.id, NotificationType::UuidMismatch)); + } } else { debug!( - "Successfully emitted UUID mismatch event for instance {}({})", + "Instance {}({}) already notified about UUID mismatch, skipping", instance.name, instance.id ); - notified_instances.insert((instance.id, NotificationType::UuidMismatch)); } + + return Ok(true); } else { debug!( "UUIDs match for instance {}({}): {}", @@ -362,7 +370,7 @@ fn check_uuid_mismatch( ); } - Ok(()) + Ok(false) } const CORE_VERSION_HEADER: &str = "defguard-core-version";