Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion new-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
"@tanstack/react-form": "^1.33.2",
"@tanstack/react-query": "^5.101.2",
"@tanstack/react-router": "^1.170.18",
"@tanstack/router-plugin": "^1.168.20",
"@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-clipboard-manager": "^2.3.2",
"@tauri-apps/plugin-dialog": "^2.7.1",
Expand Down Expand Up @@ -54,6 +53,7 @@
},
"devDependencies": {
"@tanstack/devtools-vite": "^0.8.1",
"@tanstack/router-plugin": "^1.168.20",
"@types/byte-size": "^8.1.2",
"@types/node": "^26.1.1",
"@types/react": "^19.2.17",
Expand Down
22 changes: 11 additions & 11 deletions new-ui/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions new-ui/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ minimumReleaseAgeExclude:
- react-dom@19.2.7
- react@19.2.7
- vite@8.0.16
- fast-uri@3.1.4 || 3.1.5
- js-yaml@4.3.1
2 changes: 1 addition & 1 deletion nix/package.nix
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@
inherit version pnpm;
src = ../new-ui;
fetcherVersion = 4;
hash = "sha256-UfOXEXdeCsx5gmdcmXG30db8fndQWKs6/0IzQFvRR5U=";
hash = "sha256-xEB3HmkcTaf+rxCmhZ7QWfjf13TPTN1Fvr0KU2yJliA=";
};

# Pre-build the new UI frontend so Tauri can serve it as WebviewUrl::App("compact/") and "full/".
Expand Down
7 changes: 3 additions & 4 deletions src-tauri/daemon/src/daemon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,11 @@ use defguard_client_proto::defguard::{
},
enterprise::posture::v2::DevicePostureData,
};
#[cfg(target_os = "linux")]
use defguard_client_service_locations::reconciler::{run_reconciler, ReconcileSignal};
use defguard_client_service_locations::ServiceLocationError;
#[cfg(any(windows, target_os = "linux"))]
use defguard_client_service_locations::{
reconciler::{run_reconciler, ReconcileSignal},
ServiceLocationManager,
};
use defguard_client_service_locations::ServiceLocationManager;
#[cfg(not(target_os = "macos"))]
use defguard_wireguard_rs::Kernel;
#[cfg(target_os = "macos")]
Expand Down
106 changes: 68 additions & 38 deletions src-tauri/enterprise/service-locations/src/linux.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use std::{
collections::HashSet,
fs::{self, create_dir_all, set_permissions},
os::unix::fs::PermissionsExt,
fs::{self, create_dir_all, set_permissions, OpenOptions},
io::Write,
os::unix::fs::{OpenOptionsExt, PermissionsExt},
path::PathBuf,
str::FromStr,
time::SystemTime,
Expand Down Expand Up @@ -142,7 +143,13 @@ impl ServiceLocationManager {
"Writing service location data to file: {}",
instance_file_path.display()
);
fs::write(&instance_file_path, json)?;
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.mode(SERVICE_LOCATION_FILE_PERMS)
.open(&instance_file_path)?
.write_all(json.as_bytes())?;
set_permissions(
&instance_file_path,
fs::Permissions::from_mode(SERVICE_LOCATION_FILE_PERMS),
Expand Down Expand Up @@ -241,17 +248,17 @@ impl ServiceLocationManager {
None
}

fn remove_tracked_interface(&mut self, ifname: &str) {
fn remove_tracked_interface(&mut self, ifname: &str) -> Result<(), ServiceLocationError> {
debug!("Tearing down Linux service location interface: {ifname}");
if let Some(wgapi) = self.wgapis.remove(ifname) {
if let Err(err) = wgapi.remove_interface() {
error!("Failed to remove Linux service location interface {ifname}: {err}");
} else {
debug!("Linux service location interface {ifname} removed successfully");
}
} else {
debug!("Linux service location interface {ifname} was not tracked as connected");
}
let Some(wgapi) = self.wgapis.get(ifname) else {
return Err(ServiceLocationError::InterfaceError(format!(
"Linux service location interface {ifname} is not tracked"
)));
};
wgapi.remove_interface()?;
self.wgapis.remove(ifname);
info!("Linux service location interface {ifname} removed successfully");
Ok(())
}

pub fn disconnect_service_locations_by_instance(
Expand All @@ -260,24 +267,33 @@ impl ServiceLocationManager {
) -> Result<(), ServiceLocationError> {
debug!("Disconnecting Linux service locations for instance {instance_id}");

let Some(locations) = self.connected_service_locations.remove(instance_id) else {
let Some(locations) = self.connected_service_locations.get(instance_id) else {
debug!("No connected Linux service locations found for instance {instance_id}");
return Ok(());
};
let location_pubkeys = locations
.iter()
.map(|connected| connected.location.pubkey.clone())
.collect::<Vec<_>>();

for connected in locations {
let location = connected.location;
if let Some(ifname) = self.find_interface_by_peer_pubkey(&location.pubkey) {
self.remove_tracked_interface(&ifname);
} else {
debug!(
"No Linux service location interface found for instance {instance_id}, \
location '{}'",
location.name
let mut disconnect_failed = false;
for location_pubkey in location_pubkeys {
if let Err(err) = self.disconnect_service_location(instance_id, &location_pubkey) {
error!(
"Failed to disconnect Linux service location peer {location_pubkey} for \
instance {instance_id}: {err}"
);
disconnect_failed = true;
}
}

if disconnect_failed {
return Err(ServiceLocationError::InterfaceError(format!(
"Failed to disconnect one or more Linux service locations for instance \
{instance_id}"
)));
}

Ok(())
}

Expand All @@ -299,27 +315,27 @@ impl ServiceLocationManager {
return Ok(());
};

let ifname = self.find_interface_by_peer_pubkey(location_pubkey);
let location = self.connected_service_locations[instance_id][position]
.location
.clone();
let Some(ifname) = self.find_interface_by_peer_pubkey(location_pubkey) else {
return Err(ServiceLocationError::InterfaceError(format!(
"No service location interface found for location '{}' and peer \
{location_pubkey}",
location.name
)));
};
self.remove_tracked_interface(&ifname)?;

let Some(locations) = self.connected_service_locations.get_mut(instance_id) else {
warn!("Linux service location for instance {instance_id} disappeared before removal");
return Ok(());
};
let location = locations.remove(position).location;
locations.remove(position);
if locations.is_empty() {
self.connected_service_locations.remove(instance_id);
}

if let Some(ifname) = ifname {
self.remove_tracked_interface(&ifname);
} else {
debug!(
"No Linux service location interface found for instance {instance_id}, location \
'{}'",
location.name
);
}

Ok(())
}

Expand Down Expand Up @@ -528,7 +544,7 @@ impl ServiceLocationManager {
///
/// Returns `Ok(true)` when every supported location is connected or already connected, and
/// `Ok(false)` when at least one supported location failed so the caller can retry later.
pub fn connect_to_service_locations(
pub(crate) fn connect_to_service_locations(
&mut self,
authorizations: &PostureAuthorizations,
) -> Result<bool, ServiceLocationError> {
Expand All @@ -548,8 +564,7 @@ impl ServiceLocationManager {
}

let authorization = authorizations
.get(&(instance_data.instance_id.clone(), location.pubkey.clone()))
.map(|preshared_key| preshared_key.as_deref());
.get(&(instance_data.instance_id.clone(), location.pubkey.clone()));
let action = reconcile_action(
self.is_service_location_connected(
&instance_data.instance_id,
Expand All @@ -567,6 +582,7 @@ impl ServiceLocationManager {
);
continue;
}
ReconcileAction::LeaveDisconnected => continue,
ReconcileAction::WaitForAuthorization => {
debug!(
"Leaving Linux service location '{}' disconnected: no posture check \
Expand All @@ -576,6 +592,20 @@ impl ServiceLocationManager {
all_connected = false;
continue;
}
ReconcileAction::Disconnect => {
if let Err(err) = self.disconnect_service_location(
&instance_data.instance_id,
&location.pubkey,
) {
error!(
"Failed to disconnect rejected Linux service location '{}': \
{err}",
location.name
);
all_connected = false;
}
continue;
}
ReconcileAction::Renew(preshared_key) => {
if let Err(err) = self.reapply_preshared_key(
&instance_data.instance_id,
Expand Down
Loading
Loading