Commit Graph

362 Commits

Author SHA1 Message Date
Thomas Eizinger
5141817134 feat(connlib): add reason argument to reset API (#9878)
In order to provide more detailed logs, why `connlib`'s network state is
being reset, we add a `reason` parameter that is gets logged.

Resolves: #9867
2025-07-15 13:48:33 +00:00
Jamil
12351e5985 ci: publish apple 1.5.4 clients (#9842) 2025-07-11 16:35:25 +00:00
Thomas Eizinger
f211c9d46a feat(apple): use .zip for logs (#9536)
This PR replaces the use of Apple Archive with an API that allows us to
zip the log file contents. This API doesn't handle symlinks well so we
move the symlink out of the way before making the zip. The symlink is
then moved back after the process is completed. Any errors in this
process are ignored as the symlink itself is not a critical component of
Firezone.

The zip compression is marginally less efficient than the Apple Archive.
Instead of compressing ~2GB of logs to 11.8 MB we now get an archive of
12.4 MB. Considering how much easier zip files are to handle, this seems
like a fine trade-off.

<img width="774" alt="Screenshot 2025-06-16 at 00 04 52"
src="https://github.com/user-attachments/assets/8fb6bade-5308-40b9-a446-2a2c364cb621"
/>

Resolves: #7475

---------

Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
Co-authored-by: Jamil Bou Kheir <jamilbk@users.noreply.github.com>
2025-06-23 22:25:57 +00:00
Thomas Eizinger
a2c122a3c0 refactor(apple): use guard for checking valid handle (#9614)
Follow-up to #9597
2025-06-21 21:17:01 +00:00
Jamil
ea0616e198 chore(apple): ignore generated swift bridge files (#9599)
Rust and Swift disagree about the formatting of these, leading to
constant git file dirtiness when working on Apple code.

These were originally added when we didn't have as much automation to
regeneration these on each build.
2025-06-20 18:40:48 +00:00
Jamil
5537b8cfe7 fix(apple): ensure log file exists before writing to it (#9597)
Similar to the issue for the gui clients, the log file handle needs to
be able to be rolled over after logs are cleared.

related: #6850
2025-06-20 17:03:59 +00:00
Jamil
081b075f2c chore: bump gui, apple, gateway (#9586)
The new publish automation still [has some
kinks](https://github.com/firezone/firezone/actions/runs/15764891111) so
publishing this manually.
2025-06-19 12:29:46 -07:00
Thomas Eizinger
47befe37f4 fix(apple): disable false-positive "App hang" reports (#9568)
As recommended by the Sentry team [0], "App hang" tracking should be
disabled before calling into certain system APIs like showing alerts to
prevent false-positives.

[0]: https://github.com/getsentry/sentry-cocoa/issues/3472
2025-06-18 22:44:03 +00:00
Thomas Eizinger
bb46c59f1d chore(apple): add .swift-format file (#9566)
When using alternative editors other than Xcode, Intellisense is usually
provided by `sourcekit-lsp`. The LSP server also uses `swift-format`
under the hood to format the code but appears to default to using 4
spaces for indentation. In order to standardise on how much indentation
is used, we add a `.swift-format` file that specifies is.
2025-06-18 22:40:58 +00:00
Thomas Eizinger
a06f270c7d chore(apple): un-format generated code (#9549)
It appears that the code generation in our `build.rs` generates the code
with different formatting and this therefore constantly shows up as
untracked changes in my editor.
2025-06-16 20:06:36 +00:00
Thomas Eizinger
01ad87b1c0 chore(apple): format swift code with formatter (#9535)
When working on the Swift codebase, I noticed that running the formatter
produced a massive diff. This PR re-formats the Swift code with `swift
format . --recursive --in-place` and adds a CI check to enforce it going
forward.

Resolves: #9534

---------

Co-authored-by: Jamil Bou Kheir <jamilbk@users.noreply.github.com>
2025-06-15 20:28:18 +00:00
Thomas Eizinger
dce7ffe4f0 fix(apple): drop Session in a new task (#9478)
Until we implement #3959 for the Apple client, we need to be careful
around how we de-initialise the Rust session. Callback-based designs are
difficult to get right across boundaries because they enable
re-entrances which then lead to runtime errors.

Specifically, freeing the session needs to cleanup the tokio runtime but
that is impossible if the callback is still executed from that exact
runtime. To workaround this, we need to free the session pointer from a
new task.

Moving to #3959 will solve this in a much more intuitive way because we
can ditch the callbacks and instead move to a stream of events that the
host app can consume.

---------

Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
2025-06-10 07:09:16 +00:00
Jamil
221ffc7e58 chore: publish Apple 1.5.2 (#9385) 2025-06-03 19:49:06 +00:00
Thomas Eizinger
b7b296a102 ci: apply prettier to all files (#9356)
Resolves: #8940
2025-06-02 11:12:54 +00:00
Jamil
0c0ab13b90 ci: Bump apple version to 1.5.1 (#9343) 2025-06-01 16:43:31 +00:00
Jamil
889c1a971c fix(apple): Correctly handle stopTunnel and completionHandlers (#9308)
This PR fixes two crashes related to lifetimes on Apple:

- `completionHandler` was being called from within a Task executor
context, which could be different from the one the IPC call was received
on
- The `getLogFolderSize` task could return and attempt to call
`completionHandler` after the PacketTunnelProvider deinit'd
- We were calling the completionHandler from `stopTunnel` manually.
Apple explicitly says not to do this. Instead, we must call
`cancelTunnelWithError(nil)` when we want to stop the tunnel from e.g.
the `onDisconnect`. Apple with then call our `stopTunnel` override. The
downside is that we have no control over the `NEProviderStopReason`
received in this callback, but we don't use it anyway. Instead, we write
the reason to a temporary file and read it from the GUI process when we
detect a status change to `disconnected`. When that occurs, we're able
to show a UI notification (macOS only - iOS can show this notification
from the PacketTunnelProvider itself).
2025-05-30 20:54:13 +00:00
Thomas Eizinger
c1cab32d7f feat(connlib): expose isAuthenticationError over Apple FFI (#9291)
The GUI client already uses the same function to check for
authentication errors. Therefore, this case is already handled there.

For Swift, we can easily expose this getter via the `swift-bridge`
module. For Android, we don't expose it for now. Once we tackle #3959,
this should be easier to do. In the meantime, the UX on Android is not
super terrible. The user gets signed out and we will then receive a 401
when we try to sign-in again the next time.

Resolves: #9289
2025-05-29 03:45:02 +00:00
Jamil
94b05a19f1 fix(apple): don't send connlib DNS server IPs that aren't IPs (#9242)
When pulling IPs from system resolvers, it's possible the IPv6 addresses
may contain scopes which will cause connlib to barf when parsing.

To fix these, we first convert to the Swift-native type `IPv4Address` or
`IPv6Address` and then use the string representation of those types,
which normalizes them to plain addresses.

Fixes #9055
2025-05-27 00:42:22 +00:00
Jamil
b7ec92e3aa chore(ci): Bump apple clients to 1.5.0 (#9239) 2025-05-26 08:20:05 -07:00
Jamil
b5c18db5e8 chore(ci): Bump next clients version to 1.5.0 (#9229)
We've decided we'll be bumping the minor with shipping managed
configurations support.
2025-05-26 04:24:35 +00:00
Jamil
ec682d5871 fix(apple): Don't throw when quitting with a stopped tunnel (#9231)
If the tunnel is already down when we try to quit the application, we
were throwing a harmless error because we mistakenly required a
connected status to send the `stopTunnel` command, which is just a no-op
if we're already connected.
2025-05-26 04:19:12 +00:00
Jamil
842fe8718d chore(apple): Remove managed enforcement of full-tunnel (#9230)
After discussing with @thomaseizinger, we realized this is better
supported with Polices.
2025-05-26 03:47:17 +00:00
Jamil
a7054b8f40 ci: Bump apple to 1.4.15 (#9217) 2025-05-24 12:51:27 +00:00
Jamil
e84ba4545e feat(apple): Add supportURL as managed config item (#9202)
Adds `supportURL` as a managed configuration item so that admins may
point their workforce towards their own support Resources.
2025-05-22 16:48:35 +00:00
Jamil
6fd3493ed0 refactor(apple): Consolidate configuration to host app (#9196)
On Apple platforms, `UserDefaults` provides a convenient way to store
and fetch simple plist-compatible data for your app. Unbeknownst to the
author at the time of original implementation was the fact this these
keys are already designed for managed configurations to "mask" any
user-configured equivalents.

This means we no longer need to juggle two dicts in UserDefaults, and we
can instead check which keys are forced via a simple method call.

Additionally, the implementation was simplified in the following ways:

- The host app is the "source of truth" for app configuration now. The
tunnel service receives `setConfiguration` which applies the current
configuration, and saves it in order to start up again without the GUI
connected. The obvious caveat here is that if the GUI isn't running,
configuration such as `internetResourceEnabled` applied by the
administrator won't take effect. This is considered an edge case for the
time being since no customers have asked for this. Additionally, admins
can be advised to ensure the Firezone GUI is running on the system at
all times to prevent this.
- Settings and ConfigurationManager are now able to be removed - these
became redundant after consolidating configuration to the containing
app.
2025-05-22 16:18:00 +00:00
Jamil
745b57218b chore(apple): Remove useless IPC error log (#9201)
This error case happens during normal operation, particularly when
exiting the application and can be dropped.
2025-05-22 07:19:05 +00:00
Jamil
e14c4e1eb8 refactor(apple): Only apply MDM config when changed (#9173)
In #9169 we applied MDM configuration from MDM upon _any_ change to
UserDefaults. This is unnecessary.

Instead, we can compare new vs old and only apply the new dict if
there's changes.

In this PR we also log the old and new dicts for debugging reasons.
2025-05-16 23:21:08 +00:00
Jamil
73f334e345 feat(apple): Add start on login functionality (#9168)
Adds a new settings/configuration item `startOnLogin` which simply adds
a "Login Item" which starts Firezone after signing into the Mac.

This feature is macOS 13 and above only because otherwise we will need
to bundle a helper application to register as a service to start the
app. Given our very small footprint of macOS 12 users, and how
unsupported it is, this is ok.

When it comes time to implement MDM for this feature, note that Apple
provides a means to enforce login items via the
[`ServiceManagementLoginItems`
payload](https://developer.apple.com/documentation/devicemanagement/servicemanagementmanagedloginitems)
which is outside the scope of `com.apple.configuration.managed`. This
enforces the login item in System Settings so that the user is unable to
disable it.

We also add functionality here, but bear in mind that even if we disable
the Toggle switch in our Settings page, the user could still disable the
item in system settings unless it is being set through MDM via the
service management key above.

Another thing to note is that this setting is applied on the GUI side of
the tunnel only, because it is inherently tied to the process it is
running as. We are not able to (nor does it make sense to) enable the
login item for the tunnel service. This should be fine.

Tested to ensure enabling/disabling appropriately adds and removes the
login item (and re-adds it if I manually remove it from system
settings).


Related: #8916 
Related: #2306

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
2025-05-16 22:15:49 +00:00
Jamil
f138d86494 feat(apple): Apply MDM changes to Configuration (#9169)
When the MDM installs a configuration payload to
`dev.firezone.firezone.network-extension`, the tunnel service will now
be notified of a change to its `managedDict`, applying the configuration
and updating `packetTunnelProvider`'s local copy so that it'll be
returned on the next configuration fetch from the UI.

Related: #4505
2025-05-16 22:00:16 +00:00
Jamil
9951e82727 feat(apple): Disable the update checker for MDM and App store (#9167)
For App Store installed macOS clients, it doesn't make sense to run an
update checker, because the system is managing the updates, and will
notify the user if there's an update available for Firezone (the user
has configured the system to manage app updates).

Related: #7664 
Related: #4505
2025-05-16 09:27:57 +00:00
Jamil
e599eb2f09 fix(apple): Ensure system extension loads after upgrade (#9166)
On macOS, after upgrading the client, the new system extension fails to
respond to IPC commands until it receives a `startTunnel` call. After
that, subsequent IPC calls will succeed across relaunches of the app.

To fix this, we introduce a dummy `startTunnel` call on macOS that
attempts to bring life into the System extension whenever we receive
`nil` configuration.

We also tidy up a few other things to make this easier to follow.


Fixes #9156 
Fixes #8476

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-16 07:17:12 +00:00
Jamil
7b4aa44f30 refactor(apple): Add Settings model (#9155)
The settings fields are getting tedious to manage individually, so a
helper class `Settings` is added which abstracts all of the view-related
logic applicable to user-defined settings.

When settings are saved, they are first applied to the `store`'s
existing Configuration, and then that configuration is saved via a new
consolidated IPC call `setConfiguration`.

`actorName` has been moved to a GUI-only cached store since it does not
need to live on `Configuration` any longer.

This greatly simplifies both the view logic and the IPC interface.

Notably, this does not handle the edge case where the configuration is
updated while the Settings window is open. That is saved for a later
time.
2025-05-15 23:25:00 +00:00
Jamil
7095974204 fix(apple): Handle tunnel starting before GUI after upgrade (#9141)
If the user tries to start the tunnel from system settings without
launching the GUI after upgrading to >= 1.4.15, the new configuration
will be empty, and we'll fail to set the accountSlug, preventing the
tunnel from starting.

To fix this, we add a simple convenience function that returns the
legacy configuration, and we use this configuration to start the tunnel
in case the GUI hasn't started.

Once the GUI starts, the legacy configuration is migrated and deleted,
so this is more of an edge case. Still, given the hundreds/thousands of
Apple device installations we have, someone is bound to hit it, and it
would be better to spend a few minutes saving potentially man-hours of
troubleshooting later.
2025-05-14 22:34:36 +00:00
Jamil
6fd5d96685 fix(apple): Isolate debug and release Keychain items (#9142)
On macOS, the token is saved in the system keychain so that the `root`
user is able to manage it. `secd`, the daemon that responds to Keychain
requests, is very strict about which binaries can access Keychain items
created by other binaries.

In development, the Firezone system extension runs from an unprivileged
directory, and isn't release-signed, which means it is not able to
manage the Keychain token for the release binary, and vice-versa.

To fix this, we isolate the Keychain items from each other with
different labels for `debug` and `release`, where the latter is
unchanged.

This is only an issue on debug, so a Changelog entry is not created.

Fixes #8917 
Fixes #8642
2025-05-14 22:34:11 +00:00
Jamil
204d1eb678 feat(apple): Allow user to configure connect on start (#9134)
Exposes a configuration toggle to connect on start, allowing it to be
overridden by MDM. Currently we assume this to be true.

Will need to refactor the settings soon to a dedicated
`ObservableObject` in the ViewModel to make these validations and field
checks less verbose.

related: #4505
2025-05-14 11:45:38 -07:00
Jamil
f19702f53e feat(apple): Allow user-configurable account slug (#9119)
Now that configuration is persisted in a more reasonable fashion, we can
expose a new `General` section to the Settings, allowing the user to
configure an account slug.

This field will automatically be populated upon the first sign in, so
that subsequent sign-ins will take the user directly to the account sign
in page.


Fixes #5119 
Related #8919

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-14 04:17:29 +00:00
Jamil
36e4a653f3 feat(apple/macOS): Add config to hide admin portal link (#9125)
Adds a `hideAdminPortalMenuItem` bool to our configuration to allow
admins to hide this from users.

Related: #4505
2025-05-14 04:00:50 +00:00
Jamil
5fae382375 feat(apple/macOS): Append account slug to admin portal URL link (#9126)
Appends the account slug to the admin portal URL when opened.

Related: #4505 
Related: #9119
2025-05-14 02:18:48 +00:00
Jamil
bee6479b96 refactor(apple): Use string for URL configuration (#9124)
In the UI, we need to use Strings to bind to the text inputs.
In the configuration dictionaries, we need to use Strings to save the
URLs.

It makes no sense to convert these to URLs in between. Instead, we can
validate upon save and then use them as Strings throughout.
2025-05-13 23:48:07 +00:00
Jamil
ccd6c265bb feat(apple): Allow MDM override of internet resource enabled (#9122)
All the MDM configuration to shadow the `internetResourceEnabled` state.

Related: #4505

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
2025-05-13 23:41:42 +00:00
Jamil
2d3adff590 fix(apple): Actually enable/disable internet resource (#9123)
This fixes a bug recently introduced where the actual call to connlib to
enable/disable the internet resource was removed.
2025-05-13 23:17:01 +00:00
Jamil
ae224384be feat(apple): Disable advanced settings if overridden (#9108)
If `authURL`, `apiURL`, or `logFilter` are set in the managed
configuration, we disable each of these fields respectively from user
editing.

If all of them are overridden, we disable the `Apply` and `Reset to
Defaults` buttons.

Related #4505
2025-05-13 04:06:05 +00:00
Jamil
14892d0f27 fix(apple): Save settings before signing out (#9104)
This fixes a small bug where settings wouldn't be saved if signed in,
and also allows errors during saving to bubble up to the user.

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-13 02:37:09 +00:00
Jamil
7d738bc192 refactor(apple): Fix var/func scope in SettingsView (#9106)
These are incorrectly scoped. No functionality is changing.
2025-05-13 01:35:28 +00:00
Jamil
a849711525 refactor(apple): Prepare to support MDM-shadowed config (#9084)
One simple way we can tell the GUI app which configuration fields have
been overridden by MDM is to specify an `overriddenKeys` string array.
If provided, this will disable the relevant configuration from being set
/ editable in the GUI app and communicate to the user as such.

Related: #4505

---------

Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-05-13 00:12:17 +00:00
Jamil
4cc5963af2 fix(apple): Don't double-log errors in IPCClient (#9102)
See
https://github.com/firezone/firezone/pull/9056#discussion_r2083836491
2025-05-12 22:17:53 +00:00
Jamil
08dee37d09 feat(apple): Poll tunnel for new configuration every 1s (#9083)
Similar to how we fetch new resources, we add a Configuration poller
that fetches new configuration every 1s. If the configuration is
unchanged, we respond to the caller with a cached copy to avoid needing
to serialize the data over IPC.

Related: #4505
2025-05-12 14:27:47 +00:00
Jamil
1ceccc0da0 refactor(apple): Consolidate app configuration to UserDefaults (#9056)
We are currently storing app configuration across three places:

- UserDefaults (favorite resources)
- VPN configuration (Settings)
- Disk (firezone id)

These can be consolidated to UserDefaults, which is the standard way to
store app configuration like this.

UserDefaults is the umbrella persistence store for regular app
configuration (`plist` files which are just XML dictionaries),
iCloud-synced app configuration across a user's devices, and managed app
configuration (MDM). They provide a cached, thread-safe, and
interprocess-supported mechanism for handling app configuration. We can
also subscribe to changes on this app configuration to react to changes.

Unfortunately, the System Extension ruins some of our fun because it
runs as root, and is confined to a different group container, meaning we
cannot share configuration directly between GUI and tunnel procs.

To address this, we use the tunnel process to store all vital
configuration and introduce IPC calls to set and fetch these.

Commit-by-commit review recommended, but things got a little crazy
towards the end when I realized that we can't share a single
UserDefaults between both procs.
2025-05-12 05:27:49 +00:00
Thomas Eizinger
5566f1847f refactor(rust): move crates into a more sensical hierarchy (#9066)
The current `rust/` directory is a bit of a wild-west in terms of how
the crates are organised. Most of them are simply at the top-level when
in reality, they are all `connlib`-related. The Apple and Android FFI
crates - which are entrypoints in the Rust code are defined several
layers deep.

To improve the situation, we move around and rename several crates. The
end result is that all top-level crates / directories are:

- Either entrypoints into the Rust code, i.e. applications such as
Gateway, Relay or a Client
- Or crates shared across all those entrypoints, such as `telemetry` or
`logging`
2025-05-12 01:04:17 +00:00
Jamil
283879eeb0 docs: Remove note on disabling SIP (#9075)
When developing system extensions, Apple's
[documentation](https://developer.apple.com/documentation/DriverKit/debugging-and-testing-system-extensions)
instructs developers to disable SIP and turn on system extension
developer mode to disable certain runtime checks that allow the
extension to run.

It turns out this is completely unnecessary - any properly set up Xcode
toolchain can build a functioning macOS debug client.
2025-05-12 00:55:21 +00:00