diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 65b51ef4..9be7f380 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1357,6 +1357,7 @@ dependencies = [ "dark-light", "defguard_wireguard_rs", "dirs-next", + "hyper-util", "log", "prost", "prost-build", @@ -1377,9 +1378,11 @@ dependencies = [ "thiserror 2.0.12", "time", "tokio", + "tokio-stream", "tokio-util", "tonic", "tonic-build", + "tower 0.5.2", "tracing", "tracing-appender", "tracing-subscriber", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d4180670..17526c32 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -86,6 +86,11 @@ x25519-dalek = { version = "2", features = [ "static_secrets", ] } +[target.'cfg(unix)'.dependencies] +tokio-stream = "0.1" +tower = "0.5" +hyper-util = "0.1" + [target.'cfg(windows)'.dependencies] winapi = { version = "0.3", features = ["winsvc", "winerror"] } windows-service = "0.7" diff --git a/src-tauri/src/service/mod.rs b/src-tauri/src/service/mod.rs index 1a199804..41814c49 100644 --- a/src-tauri/src/service/mod.rs +++ b/src-tauri/src/service/mod.rs @@ -6,9 +6,11 @@ pub mod utils; #[cfg(windows)] pub mod windows; +#[cfg(windows)] +use std::net::{Ipv4Addr, SocketAddr}; use std::{ collections::HashMap, - net::{IpAddr, Ipv4Addr, SocketAddr}, + net::IpAddr, pin::Pin, str::FromStr, time::{Duration, SystemTime, UNIX_EPOCH}, @@ -29,8 +31,14 @@ use proto::{ desktop_daemon_service_server::{DesktopDaemonService, DesktopDaemonServiceServer}, CreateInterfaceRequest, InterfaceData, ReadInterfaceDataRequest, RemoveInterfaceRequest, }; +#[cfg(unix)] +use std::{fs, os::unix::fs::PermissionsExt, path::Path}; use thiserror::Error; +#[cfg(unix)] +use tokio::net::UnixListener; use tokio::{sync::mpsc, time::interval}; +#[cfg(unix)] +use tokio_stream::wrappers::UnixListenerStream; use tonic::{ codegen::tokio_stream::{wrappers::ReceiverStream, Stream}, transport::Server, @@ -41,9 +49,13 @@ use tracing::{debug, error, info, info_span, Instrument}; use self::config::Config; use super::VERSION; +#[cfg(windows)] const DAEMON_HTTP_PORT: u16 = 54127; +#[cfg(windows)] pub(super) const DAEMON_BASE_URL: &str = "http://localhost:54127"; +pub(super) const DAEMON_SOCKET_PATH: &str = "/var/run/defguard.socket"; + #[derive(Error, Debug)] pub enum DaemonError { #[error(transparent)] @@ -323,16 +335,45 @@ impl DesktopDaemonService for DaemonService { } } +#[cfg(unix)] +pub async fn run_server(config: Config) -> anyhow::Result<()> { + debug!("Starting Defguard interface management daemon"); + + let daemon_service = DaemonService::new(&config); + + // Remove existing socket if it exists + if Path::new(DAEMON_SOCKET_PATH).exists() { + fs::remove_file(DAEMON_SOCKET_PATH)?; + } + + let uds = UnixListener::bind(DAEMON_SOCKET_PATH)?; + + // Set socket permissions to allow client access + // 0o666 allows read/write for owner, group, and others + fs::set_permissions(DAEMON_SOCKET_PATH, fs::Permissions::from_mode(0o666))?; + + let uds_stream = UnixListenerStream::new(uds); + + info!("Defguard daemon version {VERSION} started, listening on socket {DAEMON_SOCKET_PATH}",); + debug!("Defguard daemon configuration: {config:?}"); + + Server::builder() + .trace_fn(|_| tracing::info_span!("defguard_service")) + .add_service(DesktopDaemonServiceServer::new(daemon_service)) + .serve_with_incoming(uds_stream) + .await?; + + Ok(()) +} + +#[cfg(windows)] pub async fn run_server(config: Config) -> anyhow::Result<()> { debug!("Starting Defguard interface management daemon"); let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), DAEMON_HTTP_PORT); let daemon_service = DaemonService::new(&config); - info!( - "Defguard daemon version {} started, listening on {addr}", - VERSION - ); + info!("Defguard daemon version {VERSION} started, listening on {addr}",); debug!("Defguard daemon configuration: {config:?}"); Server::builder() diff --git a/src-tauri/src/service/utils.rs b/src-tauri/src/service/utils.rs index ed3d6034..3ca741ce 100644 --- a/src-tauri/src/service/utils.rs +++ b/src-tauri/src/service/utils.rs @@ -1,6 +1,18 @@ use std::io::stdout; +#[cfg(windows)] +use crate::service::DAEMON_BASE_URL; +#[cfg(unix)] +use crate::service::DAEMON_SOCKET_PATH; +#[cfg(unix)] +use hyper_util::rt::TokioIo; +#[cfg(unix)] +use tokio::net::UnixStream; use tonic::transport::channel::{Channel, Endpoint}; +#[cfg(unix)] +use tonic::transport::Uri; +#[cfg(unix)] +use tower::service_fn; use tracing::{debug, Level}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{ @@ -9,9 +21,22 @@ use tracing_subscriber::{ }; use crate::service::{ - proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DaemonError, DAEMON_BASE_URL, + proto::desktop_daemon_service_client::DesktopDaemonServiceClient, DaemonError, }; +#[cfg(unix)] +pub fn setup_client() -> Result, DaemonError> { + debug!("Setting up gRPC client"); + let endpoint = Endpoint::try_from("http://[::]:50051")?; + let channel = endpoint.connect_with_connector_lazy(service_fn(|_: Uri| async { + // Connect to a Uds socket + Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(DAEMON_SOCKET_PATH).await?)) + })); + let client = DesktopDaemonServiceClient::new(channel); + Ok(client) +} + +#[cfg(windows)] pub fn setup_client() -> Result, DaemonError> { debug!("Setting up gRPC client"); let endpoint = Endpoint::from_shared(DAEMON_BASE_URL)?;