diff --git a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt index 0e9052ffa..792cb2ded 100644 --- a/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt +++ b/kotlin/android/app/src/main/java/dev/firezone/android/tunnel/TunnelService.kt @@ -296,6 +296,7 @@ class TunnelService : VpnService() { osVersion = Build.VERSION.RELEASE, logDir = getLogDir(), logFilter = config.logFilter, + isInternetResourceActive = resourceState.isEnabled(), protectSocket = protectSocket, deviceInfo = gson.toJson(deviceInfo), ).use { session -> diff --git a/rust/apple-client-ffi/src/lib.rs b/rust/apple-client-ffi/src/lib.rs index 8932543e2..d50637c22 100644 --- a/rust/apple-client-ffi/src/lib.rs +++ b/rust/apple-client-ffi/src/lib.rs @@ -65,6 +65,7 @@ mod ffi { os_version_override: Option, log_dir: String, log_filter: String, + is_internet_resource_active: bool, callback_handler: CallbackHandler, device_info: String, ) -> Result; @@ -250,6 +251,7 @@ impl WrappedSession { os_version_override: Option, log_dir: String, log_filter: String, + is_internet_resource_active: bool, callback_handler: ffi::CallbackHandler, device_info: String, ) -> Result { @@ -302,6 +304,7 @@ impl WrappedSession { Arc::new(socket_factory::tcp), Arc::new(socket_factory::udp), portal, + is_internet_resource_active, runtime.handle().clone(), ); session.set_tun(Box::new(Tun::new()?)); diff --git a/rust/client-ffi/src/lib.rs b/rust/client-ffi/src/lib.rs index a7fc8f7e1..7b99712b2 100644 --- a/rust/client-ffi/src/lib.rs +++ b/rust/client-ffi/src/lib.rs @@ -94,6 +94,7 @@ impl Session { log_dir: String, log_filter: String, device_info: String, + is_internet_resource_active: bool, protect_socket: Arc, ) -> Result { let udp_socket_factory = Arc::new(protected_udp_socket_factory(protect_socket.clone())); @@ -109,6 +110,7 @@ impl Session { log_dir, log_filter, device_info, + is_internet_resource_active, tcp_socket_factory, udp_socket_factory, ) @@ -227,6 +229,7 @@ fn connect( log_dir: String, log_filter: String, device_info: String, + is_internet_resource_active: bool, tcp_socket_factory: Arc>, udp_socket_factory: Arc>, ) -> Result { @@ -278,6 +281,7 @@ fn connect( tcp_socket_factory, udp_socket_factory, portal, + is_internet_resource_active, runtime.handle().clone(), ); diff --git a/rust/client-shared/src/eventloop.rs b/rust/client-shared/src/eventloop.rs index d803ea34f..9ec4867c3 100644 --- a/rust/client-shared/src/eventloop.rs +++ b/rust/client-shared/src/eventloop.rs @@ -98,6 +98,7 @@ impl Eventloop { pub(crate) fn new( tcp_socket_factory: Arc>, udp_socket_factory: Arc>, + is_internet_resource_active: bool, mut portal: PhoenixChannel<(), IngressMessages, PublicKeyParam>, cmd_rx: mpsc::UnboundedReceiver, resource_list_sender: watch::Sender>, @@ -110,6 +111,7 @@ impl Eventloop { tcp_socket_factory, udp_socket_factory, DNS_RESOURCE_RECORDS_CACHE.lock().clone(), + is_internet_resource_active, ); portal.connect(PublicKeyParam(tunnel.public_key().to_bytes())); diff --git a/rust/client-shared/src/lib.rs b/rust/client-shared/src/lib.rs index 3bae79663..f3b9b26ff 100644 --- a/rust/client-shared/src/lib.rs +++ b/rust/client-shared/src/lib.rs @@ -58,6 +58,7 @@ impl Session { tcp_socket_factory: Arc>, udp_socket_factory: Arc>, portal: PhoenixChannel<(), IngressMessages, PublicKeyParam>, + is_internet_resource_active: bool, handle: tokio::runtime::Handle, ) -> (Self, EventStream) { let (cmd_tx, cmd_rx) = mpsc::unbounded_channel(); @@ -70,6 +71,7 @@ impl Session { Eventloop::new( tcp_socket_factory, udp_socket_factory, + is_internet_resource_active, portal, cmd_rx, resource_list_sender, diff --git a/rust/connlib/tunnel/proptest-regressions/tests.txt b/rust/connlib/tunnel/proptest-regressions/tests.txt index 2a9ec0289..27bac787a 100644 --- a/rust/connlib/tunnel/proptest-regressions/tests.txt +++ b/rust/connlib/tunnel/proptest-regressions/tests.txt @@ -208,3 +208,5 @@ cc fdfbc47a6e26893cf317a3e2dd0a95a7d07a3fc1d7c19accbf20b5582e9453e4 cc 5ca1f7f615da306a4bbcd7f1300459288600ed1740c650fa75fdefade2d789d7 cc 71380222e92940bac1d7a6ab8b13eb7180da63481c08c7c0b168c0007e02906d cc d2a87f2284b2ae5a2206bd7091aa31015599bd8301e19644a860e46c0113dafb +cc 3927e6609d6e4ec9562281e319e266d1df652b5f825ad3fe867146bab7ffa045 +cc 5f9aab8caf96d39aae4869a153a16d32193ad91a9c0095233e3a145b85780e08 diff --git a/rust/connlib/tunnel/src/client.rs b/rust/connlib/tunnel/src/client.rs index 02e7b34dc..9d528480b 100644 --- a/rust/connlib/tunnel/src/client.rs +++ b/rust/connlib/tunnel/src/client.rs @@ -38,7 +38,7 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; use std::num::NonZeroUsize; use std::ops::ControlFlow; use std::time::{Duration, Instant}; -use std::{io, iter, mem}; +use std::{io, iter}; pub(crate) const IPV4_RESOURCES: Ipv4Network = match Ipv4Network::new(Ipv4Addr::new(100, 96, 0, 0), 11) { @@ -186,7 +186,12 @@ impl PendingFlow { } impl ClientState { - pub(crate) fn new(seed: [u8; 32], records: BTreeSet, now: Instant) -> Self { + pub(crate) fn new( + seed: [u8; 32], + records: BTreeSet, + is_internet_resource_active: bool, + now: Instant, + ) -> Self { Self { resources_gateways: Default::default(), active_cidr_resources: IpNetworkTable::new(), @@ -203,7 +208,7 @@ impl ClientState { udp_dns_sockets_by_upstream_and_query_id: Default::default(), stub_resolver: StubResolver::new(records), buffered_transmits: Default::default(), - is_internet_resource_active: false, + is_internet_resource_active, recently_connected_gateways: LruCache::new(MAX_REMEMBERED_GATEWAYS), upstream_dns: Default::default(), buffered_dns_queries: Default::default(), @@ -1696,7 +1701,7 @@ impl ClientState { None => true, } } - Resource::Internet(_) => !mem::replace(&mut self.is_internet_resource_active, true), + Resource::Internet(_) => self.is_internet_resource_active, }; if activated { @@ -2167,7 +2172,7 @@ mod tests { impl ClientState { pub fn for_test() -> ClientState { - ClientState::new(rand::random(), Default::default(), Instant::now()) + ClientState::new(rand::random(), Default::default(), false, Instant::now()) } } diff --git a/rust/connlib/tunnel/src/lib.rs b/rust/connlib/tunnel/src/lib.rs index 1586cae24..8186ebd4f 100644 --- a/rust/connlib/tunnel/src/lib.rs +++ b/rust/connlib/tunnel/src/lib.rs @@ -114,6 +114,7 @@ impl ClientTunnel { tcp_socket_factory: Arc>, udp_socket_factory: Arc>, records: BTreeSet, + is_internet_resource_active: bool, ) -> Self { Self { io: Io::new( @@ -121,7 +122,12 @@ impl ClientTunnel { udp_socket_factory.clone(), BTreeSet::default(), ), - role_state: ClientState::new(rand::random(), records, Instant::now()), + role_state: ClientState::new( + rand::random(), + records, + is_internet_resource_active, + Instant::now(), + ), buffers: Buffers::default(), packet_counter: opentelemetry::global::meter("connlib") .u64_counter("system.network.packets") diff --git a/rust/connlib/tunnel/src/tests/sim_client.rs b/rust/connlib/tunnel/src/tests/sim_client.rs index 3860cb9e4..1bfb36806 100644 --- a/rust/connlib/tunnel/src/tests/sim_client.rs +++ b/rust/connlib/tunnel/src/tests/sim_client.rs @@ -105,7 +105,12 @@ impl SimClient { } } - pub(crate) fn restart(&mut self, key: PrivateKey, now: Instant) { + pub(crate) fn restart( + &mut self, + key: PrivateKey, + is_internet_resource_active: bool, + now: Instant, + ) { let dns_resource_records = self.dns_resource_record_cache.clone(); // Overwrite the ClientState with a new key. @@ -113,7 +118,12 @@ impl SimClient { // // We keep all the state in `SimClient` which is equivalent to host system. // That is where we cache resolved DNS names for example. - self.sut = ClientState::new(key.0, dns_resource_records, now); + self.sut = ClientState::new( + key.0, + dns_resource_records, + is_internet_resource_active, + now, + ); self.search_domain = None; self.dns_by_sentinel.clear(); @@ -435,8 +445,7 @@ pub struct RefClient { #[debug(skip)] resources: Vec, - #[debug(skip)] - internet_resource_active: bool, + pub(crate) internet_resource_active: bool, /// The CIDR resources the client is aware of. #[debug(skip)] @@ -492,7 +501,12 @@ impl RefClient { /// /// This simulates receiving the `init` message from the portal. pub(crate) fn init(self, now: Instant) -> SimClient { - let mut client_state = ClientState::new(self.key.0, Default::default(), now); // Cheating a bit here by reusing the key as seed. + let mut client_state = ClientState::new( + self.key.0, + Default::default(), + self.internet_resource_active, + now, + ); // Cheating a bit here by reusing the key as seed. client_state.update_interface_config(Interface { ipv4: self.tunnel_ip4, ipv6: self.tunnel_ip6, @@ -627,12 +641,13 @@ impl RefClient { pub(crate) fn add_internet_resource(&mut self, resource: InternetResource) { self.resources.push(Resource::Internet(resource.clone())); - self.internet_resource_active = true; - self.ipv4_routes - .insert(resource.id, Ipv4Network::DEFAULT_ROUTE); - self.ipv6_routes - .insert(resource.id, Ipv6Network::DEFAULT_ROUTE); + if self.internet_resource_active { + self.ipv4_routes + .insert(resource.id, Ipv4Network::DEFAULT_ROUTE); + self.ipv6_routes + .insert(resource.id, Ipv6Network::DEFAULT_ROUTE); + } } pub(crate) fn add_cidr_resource(&mut self, r: CidrResource) { @@ -656,7 +671,6 @@ impl RefClient { /// Re-adds all resources in the order they have been initially added. pub(crate) fn readd_all_resources(&mut self) { self.cidr_resources = IpNetworkTable::new(); - self.internet_resource_active = false; for resource in mem::take(&mut self.resources) { match resource { @@ -1256,6 +1270,7 @@ fn ref_client( system_dns, upstream_dns, search_domain, + any::(), client_id(), private_key(), ) @@ -1266,6 +1281,7 @@ fn ref_client( system_dns_resolvers, upstream_dns_resolvers, search_domain, + internet_resource_active, id, key, )| { @@ -1277,7 +1293,7 @@ fn ref_client( system_dns_resolvers, upstream_dns_resolvers, search_domain, - internet_resource_active: Default::default(), + internet_resource_active, cidr_resources: IpNetworkTable::new(), dns_records: Default::default(), connected_cidr_resources: Default::default(), diff --git a/rust/connlib/tunnel/src/tests/sut.rs b/rust/connlib/tunnel/src/tests/sut.rs index 81ed3f056..8c3556497 100644 --- a/rust/connlib/tunnel/src/tests/sut.rs +++ b/rust/connlib/tunnel/src/tests/sut.rs @@ -373,14 +373,12 @@ impl TunnelTest { let system_dns = ref_state.client.inner().system_dns_resolvers(); let upstream_dns = ref_state.client.inner().upstream_dns_resolvers(); let all_resources = ref_state.client.inner().all_resources(); - let internet_resource_state = ref_state.client.inner().active_internet_resource(); + let internet_resource_state = ref_state.client.inner().internet_resource_active; state.client.exec_mut(|c| { - c.restart(key, now); + c.restart(key, internet_resource_state, now); // Apply to new instance. - c.sut - .set_internet_resource_state(internet_resource_state.is_some(), now); c.sut.update_interface_config(Interface { ipv4, ipv6, diff --git a/rust/gui-client/src-tauri/src/controller.rs b/rust/gui-client/src-tauri/src/controller.rs index 54ba7506a..e057131c4 100644 --- a/rust/gui-client/src-tauri/src/controller.rs +++ b/rust/gui-client/src-tauri/src/controller.rs @@ -332,6 +332,7 @@ impl Controller { self.send_ipc(&service::ClientMsg::Connect { api_url: api_url.to_string(), token: token.expose_secret().clone(), + is_internet_resource_active: self.general_settings.internet_resource_enabled(), }) .await?; diff --git a/rust/gui-client/src-tauri/src/service.rs b/rust/gui-client/src-tauri/src/service.rs index 3e4e9874d..5000fce02 100644 --- a/rust/gui-client/src-tauri/src/service.rs +++ b/rust/gui-client/src-tauri/src/service.rs @@ -53,6 +53,7 @@ pub enum ClientMsg { Connect { api_url: String, token: String, + is_internet_resource_active: bool, }, Disconnect, ApplyLogFilter { @@ -191,6 +192,7 @@ enum Session { WaitingForNetwork { api_url: String, token: SecretString, + is_internet_resource_active: bool, }, #[default] None, @@ -372,10 +374,18 @@ impl<'a> Handler<'a> { Session::Connected { connlib, .. } => { connlib.reset("network changed".to_owned()); } - Session::WaitingForNetwork { api_url, token } => { + Session::WaitingForNetwork { + api_url, + token, + is_internet_resource_active, + } => { tracing::info!("Attempting to re-connect upon network change"); - let result = self.try_connect(&api_url.clone(), token.clone()); + let result = self.try_connect( + &api_url.clone(), + token.clone(), + *is_internet_resource_active, + ); if let Some(e) = result .as_ref() @@ -501,14 +511,18 @@ impl<'a> Handler<'a> { self.send_ipc(ServerMsg::ClearedLogs(result.map_err(|e| e.to_string()))) .await? } - ClientMsg::Connect { api_url, token } => { + ClientMsg::Connect { + api_url, + token, + is_internet_resource_active, + } => { let token = SecretString::new(token); if !self.session.is_none() { tracing::debug!(session = ?self.session, "Connecting despite existing session"); } - let result = self.try_connect(&api_url, token.clone()); + let result = self.try_connect(&api_url, token.clone(), is_internet_resource_active); if let Some(e) = result .as_ref() @@ -518,7 +532,11 @@ impl<'a> Handler<'a> { tracing::debug!( "Encountered IO error when connecting to portal, most likely we don't have Internet: {e}" ); - self.session = Session::WaitingForNetwork { api_url, token }; + self.session = Session::WaitingForNetwork { + api_url, + token, + is_internet_resource_active, + }; return Ok(()); } @@ -598,7 +616,12 @@ impl<'a> Handler<'a> { Ok(()) } - fn try_connect(&mut self, api_url: &str, token: SecretString) -> Result { + fn try_connect( + &mut self, + api_url: &str, + token: SecretString, + is_internet_resource_active: bool, + ) -> Result { let started_at = Instant::now(); let device_id = device_id::get_or_create().context("Failed to get-or-create device ID")?; @@ -636,6 +659,7 @@ impl<'a> Handler<'a> { Arc::new(tcp_socket_factory), Arc::new(UdpSocketFactory::default()), portal, + is_internet_resource_active, tokio::runtime::Handle::current(), ); diff --git a/rust/headless-client/src/main.rs b/rust/headless-client/src/main.rs index 430c50858..b4c41d59e 100644 --- a/rust/headless-client/src/main.rs +++ b/rust/headless-client/src/main.rs @@ -341,6 +341,7 @@ fn try_main() -> Result<()> { Arc::new(tcp_socket_factory), Arc::new(UdpSocketFactory::default()), portal, + false, rt.handle().clone(), ); diff --git a/swift/apple/FirezoneNetworkExtension/Adapter.swift b/swift/apple/FirezoneNetworkExtension/Adapter.swift index 3d717a91d..1b74a8c0e 100644 --- a/swift/apple/FirezoneNetworkExtension/Adapter.swift +++ b/swift/apple/FirezoneNetworkExtension/Adapter.swift @@ -209,6 +209,7 @@ class Adapter { DeviceMetadata.getOSVersion(), connlibLogFolderPath, logFilter, + internetResourceEnabled, callbackHandler, String(data: jsonEncoder.encode(DeviceMetadata.deviceInfo()), encoding: .utf8)! ) diff --git a/website/src/components/Changelog/Android.tsx b/website/src/components/Changelog/Android.tsx index 15c69e8f9..eaac418b6 100644 --- a/website/src/components/Changelog/Android.tsx +++ b/website/src/components/Changelog/Android.tsx @@ -20,7 +20,12 @@ export default function Android() { return ( {/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */} - + + + Fixes an issue where the Internet Resource could be briefly active on + startup, despite it being disabled. + + Fixes a bug that could prevent sign-ins from completing successfully diff --git a/website/src/components/Changelog/Apple.tsx b/website/src/components/Changelog/Apple.tsx index 0b9382f58..3c7e7caea 100644 --- a/website/src/components/Changelog/Apple.tsx +++ b/website/src/components/Changelog/Apple.tsx @@ -24,7 +24,12 @@ export default function Apple() { return ( {/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */} - + + + Fixes an issue where the Internet Resource could be briefly active on + startup, despite it being disabled. + + Fixes an issue where multiple concurrent Firezone macOS clients could diff --git a/website/src/components/Changelog/GUI.tsx b/website/src/components/Changelog/GUI.tsx index 9708fd5d9..c6e02aa9f 100644 --- a/website/src/components/Changelog/GUI.tsx +++ b/website/src/components/Changelog/GUI.tsx @@ -10,7 +10,12 @@ export default function GUI({ os }: { os: OS }) { return ( {/* When you cut a release, remove any solved issues from the "known issues" lists over in `client-apps`. This must not be done when the issue's PR merges. */} - + + + Fixes an issue where the Internet Resource could be briefly active on + startup, despite it being disabled. + + Fixes an issue where DNS resources would resolve to a different IP