diff --git a/.github/actions/setup-rust/action.yml b/.github/actions/setup-rust/action.yml index dd387d2cb..fcc107643 100644 --- a/.github/actions/setup-rust/action.yml +++ b/.github/actions/setup-rust/action.yml @@ -14,8 +14,14 @@ outputs: (runner.os == 'Linux' && '--help') || (runner.os == 'macOS' && '--help') || (runner.os == 'Windows' && '-p firezone-bin-shared') }} - packages: - description: Compilable / testable packages for the current OS + compile-packages: + description: Compilable packages for the current OS + value: ${{ + (runner.os == 'Linux' && '--workspace') || + (runner.os == 'macOS' && '--workspace --exclude ebpf-turn-router --exclude gui-smoke-test') || + (runner.os == 'Windows' && '-p connlib-client-shared -p connlib-model -p firezone-bin-shared -p firezone-gui-client -p firezone-gui-client-common -p firezone-headless-client -p firezone-logging -p firezone-telemetry -p firezone-tunnel -p gui-smoke-test -p http-test-server -p ip-packet -p phoenix-channel -p snownet -p socket-factory -p tun') }} + test-packages: + description: Testable packages for the current OS value: ${{ (runner.os == 'Linux' && '--workspace') || (runner.os == 'macOS' && '-p connlib-client-apple -p connlib-client-shared -p firezone-tunnel -p snownet') || diff --git a/.github/workflows/_rust.yml b/.github/workflows/_rust.yml index abfaf225c..6ea6291b4 100644 --- a/.github/workflows/_rust.yml +++ b/.github/workflows/_rust.yml @@ -62,13 +62,13 @@ jobs: with: tool: bpf-linker - run: | - cargo +${{ steps.setup-rust.outputs.nightly_version }} udeps --all-targets --all-features ${{ steps.setup-rust.outputs.packages }} + cargo +${{ steps.setup-rust.outputs.nightly_version }} udeps --all-targets --all-features ${{ steps.setup-rust.outputs.compile-packages }} name: Check for unused dependencies - run: cargo fmt -- --check - - run: cargo doc --all-features --no-deps --document-private-items ${{ steps.setup-rust.outputs.packages }} + - run: cargo doc --all-features --no-deps --document-private-items ${{ steps.setup-rust.outputs.compile-packages }} name: "cargo doc" shell: bash - - run: cargo clippy --all-targets --all-features ${{ steps.setup-rust.outputs.packages }} + - run: cargo clippy --all-targets --all-features ${{ steps.setup-rust.outputs.compile-packages }} name: "cargo clippy" shell: bash - run: cargo deny check --hide-inclusion-graph --deny unnecessary-skip @@ -114,7 +114,7 @@ jobs: set -x # First, run all tests. - cargo test --all-features ${{ steps.setup-rust.outputs.packages }} -- --include-ignored --nocapture + cargo test --all-features ${{ steps.setup-rust.outputs.test-packages }} -- --include-ignored --nocapture # Poor man's test coverage testing: Grep the generated logs for specific patterns / lines. rg --count --no-ignore SendIcmpPacket $TESTCASES_DIR diff --git a/rust/bin-shared/benches/tunnel.rs b/rust/bin-shared/benches/tunnel.rs index e36081be7..1b033b212 100644 --- a/rust/bin-shared/benches/tunnel.rs +++ b/rust/bin-shared/benches/tunnel.rs @@ -37,7 +37,6 @@ mod platform { net::UdpSocket, time::{Instant, timeout}, }; - use tun::Tun as _; pub(crate) async fn perf() -> Result<()> { const MTU: usize = 1_280; diff --git a/rust/bin-shared/src/lib.rs b/rust/bin-shared/src/lib.rs index b530a2e1f..2b879c65e 100644 --- a/rust/bin-shared/src/lib.rs +++ b/rust/bin-shared/src/lib.rs @@ -17,6 +17,12 @@ pub mod windows; #[cfg(target_os = "windows")] pub use windows as platform; +#[cfg(target_os = "macos")] +pub mod macos; + +#[cfg(target_os = "macos")] +pub use macos as platform; + pub const TOKEN_ENV_KEY: &str = "FIREZONE_TOKEN"; // wintun automatically append " Tunnel" to this @@ -40,8 +46,5 @@ pub const BUNDLE_ID: &str = "dev.firezone.client"; /// Mark for Firezone sockets to prevent routing loops on Linux. pub const FIREZONE_MARK: u32 = 0xfd002021; -#[cfg(any(target_os = "linux", target_os = "windows"))] pub use network_changes::{new_dns_notifier, new_network_notifier}; - -#[cfg(any(target_os = "linux", target_os = "windows"))] pub use tun_device_manager::TunDeviceManager; diff --git a/rust/bin-shared/src/macos.rs b/rust/bin-shared/src/macos.rs new file mode 100644 index 000000000..eb6561a61 --- /dev/null +++ b/rust/bin-shared/src/macos.rs @@ -0,0 +1,8 @@ +#[derive(clap::ValueEnum, Clone, Copy, Debug, Default)] +pub enum DnsControlMethod { + #[default] + None, +} + +pub use socket_factory::tcp as tcp_socket_factory; +pub use socket_factory::udp as udp_socket_factory; diff --git a/rust/bin-shared/src/network_changes.rs b/rust/bin-shared/src/network_changes.rs index 5e2c882bc..61e3315e5 100644 --- a/rust/bin-shared/src/network_changes.rs +++ b/rust/bin-shared/src/network_changes.rs @@ -10,5 +10,8 @@ mod imp; #[path = "network_changes/windows.rs"] mod imp; -#[cfg(any(target_os = "windows", target_os = "linux"))] +#[cfg(target_os = "macos")] +#[path = "network_changes/macos.rs"] +mod imp; + pub use imp::{new_dns_notifier, new_network_notifier}; diff --git a/rust/bin-shared/src/network_changes/macos.rs b/rust/bin-shared/src/network_changes/macos.rs new file mode 100644 index 000000000..97fc2eff3 --- /dev/null +++ b/rust/bin-shared/src/network_changes/macos.rs @@ -0,0 +1,32 @@ +use crate::platform::DnsControlMethod; +use anyhow::{Result, bail}; + +pub async fn new_dns_notifier( + _tokio_handle: tokio::runtime::Handle, + _method: DnsControlMethod, +) -> Result { + bail!("Not implemented") +} + +pub async fn new_network_notifier( + _tokio_handle: tokio::runtime::Handle, + _method: DnsControlMethod, +) -> Result { + bail!("Not implemented") +} + +pub struct Worker; + +impl Worker { + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn notified(&mut self) -> Result<()> { + bail!("Not implemented") + } + + pub fn close(self) -> Result<()> { + bail!("Not implemented") + } +} diff --git a/rust/bin-shared/src/tun_device_manager.rs b/rust/bin-shared/src/tun_device_manager.rs index 725107c44..34139643a 100644 --- a/rust/bin-shared/src/tun_device_manager.rs +++ b/rust/bin-shared/src/tun_device_manager.rs @@ -10,5 +10,9 @@ pub mod windows; #[cfg(target_os = "windows")] pub use windows as platform; -#[cfg(any(target_os = "linux", target_os = "windows"))] +#[cfg(target_os = "macos")] +pub mod macos; +#[cfg(target_os = "macos")] +pub use macos as platform; + pub use platform::TunDeviceManager; diff --git a/rust/bin-shared/src/tun_device_manager/linux.rs b/rust/bin-shared/src/tun_device_manager/linux.rs index 57f65a9f9..eee3b269b 100644 --- a/rust/bin-shared/src/tun_device_manager/linux.rs +++ b/rust/bin-shared/src/tun_device_manager/linux.rs @@ -75,8 +75,8 @@ impl TunDeviceManager { }) } - pub fn make_tun(&mut self) -> Result { - Ok(Tun::new(self.num_threads)?) + pub fn make_tun(&mut self) -> Result> { + Ok(Box::new(Tun::new(self.num_threads)?)) } #[tracing::instrument(level = "trace", skip(self))] diff --git a/rust/bin-shared/src/tun_device_manager/macos.rs b/rust/bin-shared/src/tun_device_manager/macos.rs new file mode 100644 index 000000000..86f45dc05 --- /dev/null +++ b/rust/bin-shared/src/tun_device_manager/macos.rs @@ -0,0 +1,37 @@ +use std::net::{Ipv4Addr, Ipv6Addr}; + +use anyhow::{Result, bail}; +use ip_network::{Ipv4Network, Ipv6Network}; +use tun::Tun; + +pub struct TunDeviceManager {} + +impl TunDeviceManager { + pub fn new(_mtu: usize, _num_threads: usize) -> Result { + bail!("Not implemented") + } + + pub fn make_tun(&mut self) -> Result> { + bail!("Not implemented") + } + + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn set_ips(&mut self, _ipv4: Ipv4Addr, _ipv6: Ipv6Addr) -> Result<()> { + bail!("Not implemented") + } + + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn set_routes( + &mut self, + _ipv4: Vec, + _ipv6: Vec, + ) -> Result<()> { + bail!("Not implemented") + } +} diff --git a/rust/bin-shared/src/tun_device_manager/windows.rs b/rust/bin-shared/src/tun_device_manager/windows.rs index 186aaa9cb..0abc30d86 100644 --- a/rust/bin-shared/src/tun_device_manager/windows.rs +++ b/rust/bin-shared/src/tun_device_manager/windows.rs @@ -68,12 +68,12 @@ impl TunDeviceManager { }) } - pub fn make_tun(&mut self) -> Result { + pub fn make_tun(&mut self) -> Result> { let tun = Tun::new(self.mtu)?; self.iface_idx = Some(tun.iface_idx()); self.luid = Some(tun.luid); - Ok(tun) + Ok(Box::new(tun)) } #[tracing::instrument(level = "trace", skip(self))] diff --git a/rust/dns-over-tcp/tests/smoke_server.rs b/rust/dns-over-tcp/tests/smoke_server.rs index 43d4ebb0c..a9f424712 100644 --- a/rust/dns-over-tcp/tests/smoke_server.rs +++ b/rust/dns-over-tcp/tests/smoke_server.rs @@ -37,7 +37,7 @@ async fn smoke() { let listen_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(100, 100, 111, 1), 53)); let mut dns_server = dns_over_tcp::Server::new(Instant::now()); dns_server.set_listen_addresses::(BTreeSet::from([listen_addr])); - let mut eventloop = Eventloop::new(Box::new(tun), dns_server); + let mut eventloop = Eventloop::new(tun, dns_server); tokio::spawn(std::future::poll_fn(move |cx| eventloop.poll(cx))); diff --git a/rust/gateway/Cargo.toml b/rust/gateway/Cargo.toml index 9ea1a88e6..a4d5f87fc 100644 --- a/rust/gateway/Cargo.toml +++ b/rust/gateway/Cargo.toml @@ -27,7 +27,6 @@ ip-packet = { workspace = true } ip_network = { workspace = true } libc = { workspace = true, features = ["std", "const-extern-fn", "extra_traits"] } moka = { workspace = true, features = ["future"] } -nix = { workspace = true } num_cpus = { workspace = true } opentelemetry = { workspace = true, features = ["metrics"] } opentelemetry-stdout = { workspace = true, features = ["metrics"] } @@ -50,6 +49,7 @@ uuid = { workspace = true, features = ["v4"] } [target.'cfg(target_os = "linux")'.dependencies] caps = { workspace = true } jemallocator = { workspace = true } +nix = { workspace = true } [dev-dependencies] serde_json = { workspace = true, features = ["std"] } diff --git a/rust/gateway/src/main.rs b/rust/gateway/src/main.rs index 35ea33bdb..c527330b9 100644 --- a/rust/gateway/src/main.rs +++ b/rust/gateway/src/main.rs @@ -1,4 +1,4 @@ -#[cfg(all(unix, not(target_arch = "arm")))] +#[cfg(all(target_os = "linux", not(target_arch = "arm")))] #[global_allocator] static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc; @@ -159,7 +159,7 @@ async fn try_main(cli: Cli) -> Result { let tun = tun_device_manager .make_tun() .context("Failed to create TUN device")?; - tunnel.set_tun(Box::new(tun)); + tunnel.set_tun(tun); let task = tokio::spawn(future::poll_fn({ let mut eventloop = Eventloop::new(tunnel, portal, tun_device_manager); diff --git a/rust/gui-client/src-common/src/deep_link.rs b/rust/gui-client/src-common/src/deep_link.rs index 42249fa3a..a393a89e2 100644 --- a/rust/gui-client/src-common/src/deep_link.rs +++ b/rust/gui-client/src-common/src/deep_link.rs @@ -8,6 +8,7 @@ use anyhow::{Context as _, Result, bail}; use secrecy::{ExposeSecret, SecretString}; use url::Url; +#[cfg(any(target_os = "linux", target_os = "windows"))] pub(crate) const FZ_SCHEME: &str = "firezone-fd0020211111"; #[cfg(target_os = "linux")] diff --git a/rust/gui-client/src-common/src/deep_link/macos.rs b/rust/gui-client/src-common/src/deep_link/macos.rs index 45349a491..e4c81b8d4 100644 --- a/rust/gui-client/src-common/src/deep_link/macos.rs +++ b/rust/gui-client/src-common/src/deep_link/macos.rs @@ -1,26 +1,28 @@ -//! Placeholder +use std::path::PathBuf; -use anyhow::Result; +use anyhow::{Result, bail}; use secrecy::Secret; -pub(crate) struct Server {} +pub struct Server {} impl Server { - pub(crate) fn new() -> Result { - tracing::warn!("This is not the actual Mac client"); - tracing::trace!(scheme = super::FZ_SCHEME, "prevents dead code warning"); - Ok(Self {}) + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn new() -> Result { + bail!("not implemented") } - pub(crate) async fn accept(self) -> Result>>> { + pub async fn accept(self) -> Result>>> { futures::future::pending().await } } -pub(crate) async fn open(_url: &url::Url) -> Result<()> { - Ok(()) +pub async fn open(_url: &url::Url) -> Result<()> { + bail!("not implemented") } -pub(crate) fn register() -> Result<()> { - Ok(()) +pub fn register(_path: PathBuf) -> Result<()> { + bail!("not implemented") } diff --git a/rust/gui-client/src-tauri/src/client.rs b/rust/gui-client/src-tauri/src/client.rs index ff8903a35..76ada19fe 100644 --- a/rust/gui-client/src-tauri/src/client.rs +++ b/rust/gui-client/src-tauri/src/client.rs @@ -35,7 +35,7 @@ pub(crate) fn run() -> Result<()> { // Our elevation is correct (not elevated), just run the GUI Ok(true) => run_gui(cli), Ok(false) => bail!("The GUI should run as a normal user, not elevated"), - #[cfg(not(target_os = "windows"))] // Windows elevation check never fails. + #[cfg(target_os = "linux")] // Windows/MacOS elevation check never fails. Err(error) => { show_error_dialog(&error.user_friendly_msg())?; Err(error.into()) diff --git a/rust/gui-client/src-tauri/src/client/elevation.rs b/rust/gui-client/src-tauri/src/client/elevation.rs index 9061394ff..c67b96c32 100644 --- a/rust/gui-client/src-tauri/src/client/elevation.rs +++ b/rust/gui-client/src-tauri/src/client/elevation.rs @@ -69,6 +69,19 @@ mod platform { pub(crate) enum Error {} } +#[cfg(target_os = "macos")] +mod platform { + use anyhow::Result; + + #[expect(clippy::unnecessary_wraps)] + pub(crate) fn gui_check() -> Result { + Ok(true) + } + + #[derive(Debug, Clone, Copy, thiserror::Error)] + pub(crate) enum Error {} +} + #[cfg(test)] mod tests { // Make sure it doesn't panic diff --git a/rust/gui-client/src-tauri/src/client/gui.rs b/rust/gui-client/src-tauri/src/client/gui.rs index f78f87605..a9e7418d8 100644 --- a/rust/gui-client/src-tauri/src/client/gui.rs +++ b/rust/gui-client/src-tauri/src/client/gui.rs @@ -30,10 +30,8 @@ pub(crate) mod system_tray; #[path = "gui/os_linux.rs"] mod os; -// Stub only #[cfg(target_os = "macos")] #[path = "gui/os_macos.rs"] -#[expect(clippy::unnecessary_wraps)] mod os; #[cfg(target_os = "windows")] diff --git a/rust/gui-client/src-tauri/src/client/gui/os_macos.rs b/rust/gui-client/src-tauri/src/client/gui/os_macos.rs index e030729d9..1b4a7e39c 100644 --- a/rust/gui-client/src-tauri/src/client/gui/os_macos.rs +++ b/rust/gui-client/src-tauri/src/client/gui/os_macos.rs @@ -1,23 +1,20 @@ //! This file is a stub only to do Tauri UI dev natively on a Mac. -use super::{ControllerRequest, CtlrTx}; -use anyhow::Result; -use secrecy::SecretString; +use super::CtlrTx; +use anyhow::{Result, bail}; -pub(crate) fn open_url(_app: &tauri::AppHandle, _url: &SecretString) -> Result<()> { - unimplemented!() +pub(crate) async fn set_autostart(_enabled: bool) -> Result<()> { + bail!("Not implemented") } -/// Show a notification in the bottom right of the screen -pub(crate) fn show_notification(_title: &str, _body: &str) -> Result<()> { - unimplemented!() +pub(crate) fn show_notification(_app: &tauri::AppHandle, _title: &str, _body: &str) -> Result<()> { + bail!("Not implemented") } -/// Show a notification that signals `Controller` when clicked -pub(crate) fn show_clickable_notification( - _title: &str, +pub(crate) fn show_update_notification( + _app: &tauri::AppHandle, + _ctlr_tx: CtlrTx, _body: &str, - _tx: CtlrTx, - _req: ControllerRequest, + _url: url::Url, ) -> Result<()> { - unimplemented!() + bail!("Not implemented") } diff --git a/rust/headless-client/Cargo.toml b/rust/headless-client/Cargo.toml index 59412fc29..c8cfa5557 100644 --- a/rust/headless-client/Cargo.toml +++ b/rust/headless-client/Cargo.toml @@ -58,9 +58,6 @@ resolv-conf = { workspace = true } rtnetlink = { workspace = true } sd-notify = "0.4.5" # This is a pure Rust re-implementation, so it isn't vulnerable to CVE-2024-3094 -[target.'cfg(target_os = "macos")'.dependencies] -dirs = { workspace = true } - [target.'cfg(target_os = "windows")'.dependencies] ipconfig = "0.3.2" itertools = { workspace = true } diff --git a/rust/headless-client/src/dns_control.rs b/rust/headless-client/src/dns_control.rs index 1614f8f52..1aa0b3fdd 100644 --- a/rust/headless-client/src/dns_control.rs +++ b/rust/headless-client/src/dns_control.rs @@ -19,6 +19,11 @@ mod windows; #[cfg(target_os = "windows")] use windows as platform; +#[cfg(target_os = "macos")] +mod macos; +#[cfg(target_os = "macos")] +use macos as platform; + use platform::system_resolvers; /// Controls system-wide DNS. diff --git a/rust/headless-client/src/dns_control/macos.rs b/rust/headless-client/src/dns_control/macos.rs new file mode 100644 index 000000000..a9de71d24 --- /dev/null +++ b/rust/headless-client/src/dns_control/macos.rs @@ -0,0 +1,32 @@ +use std::net::IpAddr; + +use super::DnsController; +use anyhow::{Result, bail}; +use dns_types::DomainName; +use firezone_bin_shared::macos::DnsControlMethod; + +impl DnsController { + pub fn deactivate(&mut self) -> Result<()> { + bail!("Not implemented") + } + + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn set_dns( + &mut self, + _dns_config: Vec, + _search_domain: Option, + ) -> Result<()> { + bail!("Not implemented") + } + + pub fn flush(&self) -> Result<()> { + bail!("Not implemented") + } +} + +pub(crate) fn system_resolvers(_dns_control_method: DnsControlMethod) -> Result> { + bail!("Not implemented") +} diff --git a/rust/headless-client/src/ipc_service.rs b/rust/headless-client/src/ipc_service.rs index 4691cbcc0..0e643dbbc 100644 --- a/rust/headless-client/src/ipc_service.rs +++ b/rust/headless-client/src/ipc_service.rs @@ -30,7 +30,7 @@ use std::{ time::Duration, }; use tokio::{sync::mpsc, time::Instant}; -use tracing_subscriber::{EnvFilter, Layer, Registry, layer::SubscriberExt}; +use tracing_subscriber::{Layer, Registry, layer::SubscriberExt}; use url::Url; pub mod ipc; @@ -48,13 +48,9 @@ pub mod platform; #[path = "ipc_service/windows.rs"] pub mod platform; -/// Default log filter for the IPC service -#[cfg(debug_assertions)] -const SERVICE_RUST_LOG: &str = "debug"; - -/// Default log filter for the IPC service -#[cfg(not(debug_assertions))] -const SERVICE_RUST_LOG: &str = "info"; +#[cfg(target_os = "macos")] +#[path = "ipc_service/macos.rs"] +pub mod platform; #[derive(clap::Parser)] #[command(author, version, about, long_about = None)] @@ -616,7 +612,7 @@ impl<'a> Handler<'a> { .make_tun() .context("Failed to create TUN device")? }; - connlib.set_tun(Box::new(tun)); + connlib.set_tun(tun); let session = Session { cb_rx, connlib }; self.session = Some(session); @@ -652,7 +648,7 @@ fn setup_logging( std::fs::create_dir_all(&log_dir) .context("We should have permissions to create our log dir")?; - let directives = get_log_filter().context("Couldn't read log filter")?; + let directives = crate::get_log_filter().context("Couldn't read log filter")?; let (file_filter, file_reloader) = firezone_logging::try_filter(&directives)?; let (stdout_filter, stdout_reloader) = firezone_logging::try_filter(&directives)?; @@ -679,31 +675,6 @@ fn setup_logging( Ok((file_handle, file_reloader.merge(stdout_reloader))) } -/// Reads the log filter for the IPC service or for debug commands -/// -/// e.g. `info` -/// -/// Reads from: -/// 1. `RUST_LOG` env var -/// 2. `known_dirs::ipc_log_filter()` file -/// 3. Hard-coded default `SERVICE_RUST_LOG` -/// -/// Errors if something is badly wrong, e.g. the directory for the config file -/// can't be computed -pub(crate) fn get_log_filter() -> Result { - if let Ok(filter) = std::env::var(EnvFilter::DEFAULT_ENV) { - return Ok(filter); - } - - if let Ok(filter) = - std::fs::read_to_string(known_dirs::ipc_log_filter()?).map(|s| s.trim().to_string()) - { - return Ok(filter); - } - - Ok(SERVICE_RUST_LOG.to_string()) -} - #[cfg(test)] mod tests { use super::{Cli, Cmd}; diff --git a/rust/headless-client/src/ipc_service/ipc.rs b/rust/headless-client/src/ipc_service/ipc.rs index 8ae2d604b..5123d4bb7 100644 --- a/rust/headless-client/src/ipc_service/ipc.rs +++ b/rust/headless-client/src/ipc_service/ipc.rs @@ -16,6 +16,10 @@ mod platform; #[path = "ipc/windows.rs"] pub mod platform; +#[cfg(target_os = "macos")] +#[path = "ipc/macos.rs"] +pub mod platform; + pub(crate) use platform::Server; use platform::{ClientStream, ServerStream}; diff --git a/rust/headless-client/src/ipc_service/ipc/macos.rs b/rust/headless-client/src/ipc_service/ipc/macos.rs new file mode 100644 index 000000000..5cb6f0ce0 --- /dev/null +++ b/rust/headless-client/src/ipc_service/ipc/macos.rs @@ -0,0 +1,34 @@ +use super::ServiceId; +use anyhow::{Result, bail}; +use tokio::net::UnixStream; + +pub(crate) struct Server {} + +pub type ClientStream = UnixStream; +pub(crate) type ServerStream = UnixStream; + +#[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" +)] +pub async fn connect_to_service(_id: ServiceId) -> Result { + bail!("not implemented") +} + +impl Server { + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub(crate) async fn new(_id: ServiceId) -> Result { + bail!("not implemented") + } + + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub(crate) async fn next_client(&mut self) -> Result { + bail!("not implemented") + } +} diff --git a/rust/headless-client/src/ipc_service/macos.rs b/rust/headless-client/src/ipc_service/macos.rs new file mode 100644 index 000000000..8e3795561 --- /dev/null +++ b/rust/headless-client/src/ipc_service/macos.rs @@ -0,0 +1,17 @@ +use super::CliCommon; +use anyhow::{Result, bail}; + +pub(crate) fn run_ipc_service(cli: CliCommon) -> Result<()> { + // We call this here to avoid a dead-code warning. + let (_handle, _log_filter_reloader) = super::setup_logging(cli.log_dir)?; + + bail!("not implemented") +} + +pub(crate) fn elevation_check() -> Result { + bail!("not implemented") +} + +pub(crate) fn install_ipc_service() -> Result<()> { + bail!("not implemented") +} diff --git a/rust/headless-client/src/known_dirs/macos.rs b/rust/headless-client/src/known_dirs/macos.rs index cfaff77aa..91c5169b6 100644 --- a/rust/headless-client/src/known_dirs/macos.rs +++ b/rust/headless-client/src/known_dirs/macos.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + pub fn ipc_service_config() -> Option { unimplemented!() } diff --git a/rust/headless-client/src/lib.rs b/rust/headless-client/src/lib.rs index f275a40d3..76c8a59c9 100644 --- a/rust/headless-client/src/lib.rs +++ b/rust/headless-client/src/lib.rs @@ -21,7 +21,7 @@ use std::{ path::PathBuf, }; use tokio::sync::mpsc; -use tracing_subscriber::{Layer as _, Registry, fmt, layer::SubscriberExt as _}; +use tracing_subscriber::{EnvFilter, Layer as _, Registry, fmt, layer::SubscriberExt as _}; mod clear_logs; /// Generate a persistent device ID, stores it to disk, and reads it back. @@ -57,6 +57,10 @@ pub struct CliCommon { #[arg(long, env = "FIREZONE_DNS_CONTROL", default_value = "nrpt")] pub dns_control: DnsControlMethod, + #[cfg(target_os = "macos")] + #[arg(long, env = "FIREZONE_DNS_CONTROL", default_value = "none")] + pub dns_control: DnsControlMethod, + /// File logging directory. Should be a path that's writeable by the current user. #[arg(short, long, env = "LOG_DIR")] pub log_dir: Option, @@ -135,7 +139,7 @@ impl Callbacks for CallbackHandler { /// Sets up logging for stdout only, with INFO level by default pub fn setup_stdout_logging() -> Result { - let directives = ipc_service::get_log_filter().context("Can't read log filter")?; + let directives = get_log_filter().context("Can't read log filter")?; let (filter, reloader) = firezone_logging::try_filter(&directives)?; let layer = fmt::layer() .event_format(firezone_logging::Format::new()) @@ -146,6 +150,36 @@ pub fn setup_stdout_logging() -> Result { Ok(reloader) } +/// Reads the log filter for the IPC service or for debug commands +/// +/// e.g. `info` +/// +/// Reads from: +/// 1. `RUST_LOG` env var +/// 2. `known_dirs::ipc_log_filter()` file +/// 3. Hard-coded default `SERVICE_RUST_LOG` +/// +/// Errors if something is badly wrong, e.g. the directory for the config file +/// can't be computed +pub(crate) fn get_log_filter() -> Result { + #[cfg(not(debug_assertions))] + const DEFAULT_LOG_FILTER: &str = "info"; + #[cfg(debug_assertions)] + const DEFAULT_LOG_FILTER: &str = "debug"; + + if let Ok(filter) = std::env::var(EnvFilter::DEFAULT_ENV) { + return Ok(filter); + } + + if let Ok(filter) = + std::fs::read_to_string(known_dirs::ipc_log_filter()?).map(|s| s.trim().to_string()) + { + return Ok(filter); + } + + Ok(DEFAULT_LOG_FILTER.to_string()) +} + #[cfg(test)] mod tests { use super::*; diff --git a/rust/headless-client/src/macos.rs b/rust/headless-client/src/macos.rs new file mode 100644 index 000000000..7d111ffcd --- /dev/null +++ b/rust/headless-client/src/macos.rs @@ -0,0 +1,15 @@ +use std::path::{Path, PathBuf}; + +use anyhow::{Result, bail}; + +pub(crate) fn check_token_permissions(_path: &Path) -> Result<()> { + bail!("Not implemented") +} + +pub(crate) fn default_token_path() -> PathBuf { + PathBuf::from("/etc/dummy") +} + +pub(crate) fn notify_service_controller() -> Result<()> { + bail!("Not implemented") +} diff --git a/rust/headless-client/src/main.rs b/rust/headless-client/src/main.rs index a2add9c32..7f9fecf88 100644 --- a/rust/headless-client/src/main.rs +++ b/rust/headless-client/src/main.rs @@ -36,6 +36,10 @@ mod platform; #[path = "windows.rs"] mod platform; +#[cfg(target_os = "macos")] +#[path = "macos.rs"] +mod platform; + use platform::default_token_path; /// Command-line args for the headless Client @@ -260,7 +264,7 @@ fn main() -> Result<()> { tun_device.make_tun()? }; - session.set_tun(Box::new(tun)); + session.set_tun(tun); session.set_dns(dns_controller.system_resolvers()); drop(connect_span); diff --git a/rust/headless-client/src/signals.rs b/rust/headless-client/src/signals.rs index e820b6b18..e541b63ff 100644 --- a/rust/headless-client/src/signals.rs +++ b/rust/headless-client/src/signals.rs @@ -6,4 +6,8 @@ mod platform; #[path = "signals/windows.rs"] mod platform; +#[cfg(target_os = "macos")] +#[path = "signals/macos.rs"] +mod platform; + pub use platform::{Hangup, Terminate}; diff --git a/rust/headless-client/src/signals/macos.rs b/rust/headless-client/src/signals/macos.rs new file mode 100644 index 000000000..7f56832ae --- /dev/null +++ b/rust/headless-client/src/signals/macos.rs @@ -0,0 +1,34 @@ +use anyhow::{Result, bail}; +use futures::task::{Context, Poll}; + +pub struct Terminate {} + +pub struct Hangup {} + +impl Terminate { + pub fn new() -> Result { + bail!("Not implemented") + } + + pub fn poll_recv(&mut self, _cx: &mut Context<'_>) -> Poll<()> { + Poll::Pending + } + + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn recv(&mut self) {} +} + +impl Hangup { + pub fn new() -> Result { + bail!("Not implemented") + } + + #[expect( + clippy::unused_async, + reason = "Signture must match other operating systems" + )] + pub async fn recv(&mut self) {} +} diff --git a/rust/headless-client/src/uptime/mod.rs b/rust/headless-client/src/uptime/mod.rs index 682a0d935..455fa2cd7 100644 --- a/rust/headless-client/src/uptime/mod.rs +++ b/rust/headless-client/src/uptime/mod.rs @@ -45,6 +45,13 @@ pub fn get() -> Option { Some(Duration::from_millis(ret)) } +#[cfg(target_os = "macos")] +pub fn get() -> Option { + debug_assert!(false, "Not implemented on macOS"); + + None +} + #[cfg(test)] mod tests { #[test] diff --git a/rust/relay/server/Cargo.toml b/rust/relay/server/Cargo.toml index ad6075d3b..c0de9e93f 100644 --- a/rust/relay/server/Cargo.toml +++ b/rust/relay/server/Cargo.toml @@ -62,7 +62,7 @@ opentelemetry_sdk = { workspace = true, features = ["testing"] } test-strategy = { workspace = true } tokio = { workspace = true, features = ["process", "macros", "net"] } -[build-dependencies] +[target.'cfg(target_os = "linux")'.build-dependencies] anyhow = "1" aya-build = { workspace = true } diff --git a/rust/relay/server/src/ebpf/stub.rs b/rust/relay/server/src/ebpf/stub.rs index 88b3d7a1b..c86b2ddc1 100644 --- a/rust/relay/server/src/ebpf/stub.rs +++ b/rust/relay/server/src/ebpf/stub.rs @@ -12,11 +12,11 @@ use crate::{AllocationPort, ClientSocket, PeerSocket}; pub struct Program {} impl Program { - pub fn try_load(_: &'static str) -> Result { + pub fn try_load(_: &str) -> Result { Err(anyhow::anyhow!("Platform not supported")) } - pub fn add_channel_binding_ipv4( + pub fn add_channel_binding( &mut self, _: ClientSocket, _: ChannelNumber, @@ -26,7 +26,7 @@ impl Program { Ok(()) } - pub fn remove_channel_binding_ipv4( + pub fn remove_channel_binding( &mut self, _: ClientSocket, _: ChannelNumber, diff --git a/rust/relay/server/src/main.rs b/rust/relay/server/src/main.rs index 103151298..d413ffe27 100644 --- a/rust/relay/server/src/main.rs +++ b/rust/relay/server/src/main.rs @@ -1,6 +1,6 @@ #![cfg_attr(test, allow(clippy::unwrap_used))] -#[cfg(all(unix, not(target_arch = "arm")))] +#[cfg(all(target_os = "linux", not(target_arch = "arm")))] #[global_allocator] static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;