mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(clients): set Internet Resource state on startup (#10509)
Building on top of #10507, setting the initial Internet Resource state is a piece of cake. All we need to do is thread a boolean variable through to all call-sites of `Session::connect`. Without the need for the Internet Resource's ID, we can simply pass in the boolean that is saved in the configuration of each client. Resolves: #10255
This commit is contained in:
@@ -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 ->
|
||||
|
||||
@@ -65,6 +65,7 @@ mod ffi {
|
||||
os_version_override: Option<String>,
|
||||
log_dir: String,
|
||||
log_filter: String,
|
||||
is_internet_resource_active: bool,
|
||||
callback_handler: CallbackHandler,
|
||||
device_info: String,
|
||||
) -> Result<WrappedSession, String>;
|
||||
@@ -250,6 +251,7 @@ impl WrappedSession {
|
||||
os_version_override: Option<String>,
|
||||
log_dir: String,
|
||||
log_filter: String,
|
||||
is_internet_resource_active: bool,
|
||||
callback_handler: ffi::CallbackHandler,
|
||||
device_info: String,
|
||||
) -> Result<Self> {
|
||||
@@ -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()?));
|
||||
|
||||
@@ -94,6 +94,7 @@ impl Session {
|
||||
log_dir: String,
|
||||
log_filter: String,
|
||||
device_info: String,
|
||||
is_internet_resource_active: bool,
|
||||
protect_socket: Arc<dyn ProtectSocket>,
|
||||
) -> Result<Self, Error> {
|
||||
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<dyn SocketFactory<TcpSocket>>,
|
||||
udp_socket_factory: Arc<dyn SocketFactory<UdpSocket>>,
|
||||
) -> Result<Session, Error> {
|
||||
@@ -278,6 +281,7 @@ fn connect(
|
||||
tcp_socket_factory,
|
||||
udp_socket_factory,
|
||||
portal,
|
||||
is_internet_resource_active,
|
||||
runtime.handle().clone(),
|
||||
);
|
||||
|
||||
|
||||
@@ -98,6 +98,7 @@ impl Eventloop {
|
||||
pub(crate) fn new(
|
||||
tcp_socket_factory: Arc<dyn SocketFactory<TcpSocket>>,
|
||||
udp_socket_factory: Arc<dyn SocketFactory<UdpSocket>>,
|
||||
is_internet_resource_active: bool,
|
||||
mut portal: PhoenixChannel<(), IngressMessages, PublicKeyParam>,
|
||||
cmd_rx: mpsc::UnboundedReceiver<Command>,
|
||||
resource_list_sender: watch::Sender<Vec<ResourceView>>,
|
||||
@@ -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()));
|
||||
|
||||
@@ -58,6 +58,7 @@ impl Session {
|
||||
tcp_socket_factory: Arc<dyn SocketFactory<TcpSocket>>,
|
||||
udp_socket_factory: Arc<dyn SocketFactory<UdpSocket>>,
|
||||
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,
|
||||
|
||||
@@ -208,3 +208,5 @@ cc fdfbc47a6e26893cf317a3e2dd0a95a7d07a3fc1d7c19accbf20b5582e9453e4
|
||||
cc 5ca1f7f615da306a4bbcd7f1300459288600ed1740c650fa75fdefade2d789d7
|
||||
cc 71380222e92940bac1d7a6ab8b13eb7180da63481c08c7c0b168c0007e02906d
|
||||
cc d2a87f2284b2ae5a2206bd7091aa31015599bd8301e19644a860e46c0113dafb
|
||||
cc 3927e6609d6e4ec9562281e319e266d1df652b5f825ad3fe867146bab7ffa045
|
||||
cc 5f9aab8caf96d39aae4869a153a16d32193ad91a9c0095233e3a145b85780e08
|
||||
|
||||
@@ -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<DnsResourceRecord>, now: Instant) -> Self {
|
||||
pub(crate) fn new(
|
||||
seed: [u8; 32],
|
||||
records: BTreeSet<DnsResourceRecord>,
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,6 +114,7 @@ impl ClientTunnel {
|
||||
tcp_socket_factory: Arc<dyn SocketFactory<TcpSocket>>,
|
||||
udp_socket_factory: Arc<dyn SocketFactory<UdpSocket>>,
|
||||
records: BTreeSet<DnsResourceRecord>,
|
||||
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")
|
||||
|
||||
@@ -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<Resource>,
|
||||
|
||||
#[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::<bool>(),
|
||||
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(),
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -332,6 +332,7 @@ impl<I: GuiIntegration> Controller<I> {
|
||||
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?;
|
||||
|
||||
|
||||
@@ -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<Session> {
|
||||
fn try_connect(
|
||||
&mut self,
|
||||
api_url: &str,
|
||||
token: SecretString,
|
||||
is_internet_resource_active: bool,
|
||||
) -> Result<Session> {
|
||||
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(),
|
||||
);
|
||||
|
||||
|
||||
@@ -341,6 +341,7 @@ fn try_main() -> Result<()> {
|
||||
Arc::new(tcp_socket_factory),
|
||||
Arc::new(UdpSocketFactory::default()),
|
||||
portal,
|
||||
false,
|
||||
rt.handle().clone(),
|
||||
);
|
||||
|
||||
|
||||
@@ -209,6 +209,7 @@ class Adapter {
|
||||
DeviceMetadata.getOSVersion(),
|
||||
connlibLogFolderPath,
|
||||
logFilter,
|
||||
internetResourceEnabled,
|
||||
callbackHandler,
|
||||
String(data: jsonEncoder.encode(DeviceMetadata.deviceInfo()), encoding: .utf8)!
|
||||
)
|
||||
|
||||
@@ -20,7 +20,12 @@ export default function Android() {
|
||||
return (
|
||||
<Entries downloadLinks={downloadLinks} title="Android">
|
||||
{/* 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. */}
|
||||
<Unreleased></Unreleased>
|
||||
<Unreleased>
|
||||
<ChangeItem pull="10509">
|
||||
Fixes an issue where the Internet Resource could be briefly active on
|
||||
startup, despite it being disabled.
|
||||
</ChangeItem>
|
||||
</Unreleased>
|
||||
<Entry version="1.5.4" date={new Date("2025-09-18")}>
|
||||
<ChangeItem pull="10371">
|
||||
Fixes a bug that could prevent sign-ins from completing successfully
|
||||
|
||||
@@ -24,7 +24,12 @@ export default function Apple() {
|
||||
return (
|
||||
<Entries downloadLinks={downloadLinks} title="macOS / iOS">
|
||||
{/* 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. */}
|
||||
<Unreleased></Unreleased>
|
||||
<Unreleased>
|
||||
<ChangeItem pull="10509">
|
||||
Fixes an issue where the Internet Resource could be briefly active on
|
||||
startup, despite it being disabled.
|
||||
</ChangeItem>
|
||||
</Unreleased>
|
||||
<Entry version="1.5.8" date={new Date("2025-09-10")}>
|
||||
<ChangeItem pull="10313">
|
||||
Fixes an issue where multiple concurrent Firezone macOS clients could
|
||||
|
||||
@@ -10,7 +10,12 @@ export default function GUI({ os }: { os: OS }) {
|
||||
return (
|
||||
<Entries downloadLinks={downloadLinks(os)} title={title(os)}>
|
||||
{/* 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. */}
|
||||
<Unreleased></Unreleased>
|
||||
<Unreleased>
|
||||
<ChangeItem pull="10509">
|
||||
Fixes an issue where the Internet Resource could be briefly active on
|
||||
startup, despite it being disabled.
|
||||
</ChangeItem>
|
||||
</Unreleased>
|
||||
<Entry version="1.5.7" date={new Date("2025-09-10")}>
|
||||
<ChangeItem pull="10104">
|
||||
Fixes an issue where DNS resources would resolve to a different IP
|
||||
|
||||
Reference in New Issue
Block a user