When adding a new Resource that has the same address as a previous
Resource, we would fail to call `emit_resources_changed`, and the
Resource would fail to show up in the client's resource list.
This happened because we essentially didn't consider "activating" the
resource if the resource address didn't change.
With this PR, we always do the following:
- DNS Resource: Add address to the stub resolver -> no-op if address
exists
- CIDR Resource: `maybe_update_cidr_resources` -> no-op if duplicate
CIDR is added
- Internet Resource: No-op if resource ID doesn't change (it shouldn't
ever)
Since we remove the early-exit logic, the `maybe_update_tun_routes` and
`emit_resources_changed` is always called.
`maybe_update_tun_routes` is a no-op if the address hasn't changed, so
the early-exit logic to avoid calling that seems to be redundant.
## Tested:
- [x] Adding / removing a resource
- [x] Updating a resource's fields individually, observing the client
resource updates properly
- [x] Adding two CIDR resources with the same address, observing that
the routing table _was not updated_ (thus no disruption to packet
flows).
Fixes#8100
Before, we would receive an `NSError` object and the type-matching
wouldn't take effect at all, causing the default alert to show every
time. This solves that by introducing a `UserFriendlyError` protocol
which is more robust against the two main `Error` and `NSError`
variants.
A particular version of Xcode locks in particular versions of SDKs to
build against. If we hardcode this, the benefit is that we have a
predictable and repeatable build environment.
The downside is whenever GitHub updates its macOS runner images, we
could fail to build due to a version mismatch.
In general, drift between Xcode versions isn't a problem, and tracking
the latest will more closely track developer's machines.
The current `VPNConfigurationManager` class is too large and handles 3
separate things:
- Encoding IPC messages
- Sending IPC messages
- Loading/storing the VPN configuration
With this PR, these are split out into:
- ProviderMessage class
- IPCClient class
- VPNConfigurationManager class
These are then use directly from our `Store` in order to load their
state upon `init()`, set the relevant properties, and thus views are
updated accordingly.
A couple minor bugs are fixed as well as part of the refactor.
### Tested: macOS
- [x] Sign in
- [x] Sign out
- [x] Yanking the VPN configuration while signed in / out
- [x] Yanking the system extension while signed in / out
- [x] Denying the VPN configuration
- [x] Denying Notifications
- [x] Denying System Extension
### Tested: iOS
- [x] Sign in
- [x] Sign out
- [x] Yanking the VPN configuration while signed in / out
- [x] Yanking the system extension while signed in / out
- [x] Denying the VPN configuration
- [x] Denying Notifications
---------
Signed-off-by: Jamil <jamilbk@users.noreply.github.com>
Whenever a Resource's name, address_description, or assigned sites
change, it is not currently reflected in clients. For that to happen the
address is changed.
This PR updates that behavior so that if any display fields are changed,
the `on_update_resources` callback is called which properly updates the
resource list views in clients.
Fixes#8284
In order to properly handle SRV and TXT records on the clients, we need
to be able to pick a Gateway using the initial query itself. After that,
we need to know the Gateway Tunnel IPs we're connecting to so we can
have the query perform the lookup.
Fixes#8281
At present, `connlib` can process a resource list gracefully that
handles unknown resource types. If a known type fails to match the
schema however, we fail to deserialise the entire list.
To reduce the blast radius of potential bugs here, we accept everything
that is valid JSON as the "value" of a resource. Only when processing
the individual items will we attempt to deserialise it into the expected
model, skipping any resources that cannot be deserialised.
At present, Clients are only allowed to send packets to resources
accessible via the Gateway but not to the Gateway itself. Thus, any
application (including Firezone itself) that opens a listening socket on
the TUN device will never receive any traffic.
This has opens up interesting features like hosting additional services
on the machine that the Gateway is running on. Concretely, in order to
implement #8221, we will run a DNS server on port 53 of the TUN device
as part of the Gateway.
The diff for this ended up being a bit larger because we are introducing
an `IpConfig` abstraction so we don't have to track 4 IP addresses as
separate fields within `ClientOnGateway`; the connection-specific state
on a Gateway. This is where we allow / deny traffic from a Client. To
allow traffic for this particular Gateway, we need to know our own TUN
IP configuration within the component.
We had a number of validation issues:
- DNS resources allow address `1.1.1.1` or `1.1.1.1/32`. These are not
valid and will cause issues during resolution.
- IP resources were allowing basically any string character on `edit`
caused by a logic bug in the changeset
- CIDR resources, same as above
- `*.*.*.*.google.com` and similar DNS wildcard resources were not
allowed
This PR beefs all of those up so that we have a higher degree of
certainty that our data is valid. If invalid data reaches connlib, it
will cause a panic.
This PR also introduces a migration to migrate any invalid resources
into the proper format in the DB.
Fixes#8287
Why:
* After merging #8267 it was discovered that there was a race condition
that allowed a `resource_create` message to end up at the Gateway
Channel process. Previously, this message would not have ever arrived,
because we were replacing Resource IDs when a breaking change was made,
but since that is no longer the case, it is possible that a connection
could be established between the time the `delete_resource` and
`create_resource` messages are sent and the `create_resource` would end
up at the Gateway Channel process. This commit adds a no-op handler to
make sure the message gets processed without throwing an error.
Why:
* Rather than using a persistent_id field in Resources/Policies, it was
decided that we should allow "breaking changes" to these entities. This
means that Resources/Policies will now be able to update all fields on
the schema without changing the primary key ID of the entity.
* This change will greatly help the API and Terraform provider
development.
@jamilbk, would you like me to put a migration in this PR to actually
get rid of all of the existing soft deleted entities?
@thomaseizinger, I tagged you on this, because I wanted to make sure
that these changes weren't going to break any expectations in the client
and/or gateways.
---------
Signed-off-by: Brian Manifold <bmanifold@users.noreply.github.com>
Co-authored-by: Jamil <jamilbk@users.noreply.github.com>
Currently, we wait for the first "backoff" duration when the WebSocket
disconnects. Instead, we should just try to reconnect immediately and
only wait if we hit another error.
Whilst we are establishing a connection, the host network stack may run
into timeouts and retransmit packets. Buffering these copies doesn't
make any sense because we are then just flooding the remote with e.g. 4
TCP SYNs for the same connection.
This check is O(N) with the number of buffered packets. Those are at
most a few dozens so there shouldn't be a need for anything more
efficient.
This effectively reverts #8223 due to how this interacts with the
generated packages on Linux. The _package_ itself should still be called
`firezone-client-gui` because that is what we are installing. Perhaps we
will one day add a headless-client package so the naming chosen here
should allow for that.
To customize the desktop entry, we instead make use of the
`desktopTemplate` configuration of the Tauri bundler where we can
provide a custom `.desktop` file where we can specify a particular
application name.
As part of this, we are also updating the docs on the website to mention
the new name `Firezone Client`.
Bumps [uuid](https://github.com/uuid-rs/uuid) from 1.11.0 to 1.14.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/uuid-rs/uuid/releases">uuid's
releases</a>.</em></p>
<blockquote>
<h2>v1.14.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Add FromStr impls to the fmt structs by <a
href="https://github.com/tysen"><code>@tysen</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/806">uuid-rs/uuid#806</a></li>
<li>Prepare for 1.14.0 release by <a
href="https://github.com/KodrAus"><code>@KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/807">uuid-rs/uuid#807</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/tysen"><code>@tysen</code></a> made
their first contribution in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/806">uuid-rs/uuid#806</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/uuid-rs/uuid/compare/v1.13.2...v1.14.0">https://github.com/uuid-rs/uuid/compare/v1.13.2...v1.14.0</a></p>
<h2>v1.13.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Add a compile_error when no source of randomness is available on
wasm32-unknown-unknown by <a
href="https://github.com/KodrAus"><code>@KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/804">uuid-rs/uuid#804</a></li>
<li>Prepare for 1.13.2 release by <a
href="https://github.com/KodrAus"><code>@KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/805">uuid-rs/uuid#805</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/uuid-rs/uuid/compare/1.13.1...v1.13.2">https://github.com/uuid-rs/uuid/compare/1.13.1...v1.13.2</a></p>
<h2>1.13.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix <code>wasm32</code> with <code>atomics</code> by <a
href="https://github.com/bushrat011899"><code>@bushrat011899</code></a>
in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/797">uuid-rs/uuid#797</a></li>
<li>Prepare for 1.13.1 release by <a
href="https://github.com/KodrAus"><code>@KodrAus</code></a> in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/799">uuid-rs/uuid#799</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/bushrat011899"><code>@bushrat011899</code></a>
made their first contribution in <a
href="https://redirect.github.com/uuid-rs/uuid/pull/797">uuid-rs/uuid#797</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/uuid-rs/uuid/compare/1.13.0...1.13.1">https://github.com/uuid-rs/uuid/compare/1.13.0...1.13.1</a></p>
<h2>1.13.0</h2>
<h2>⚠️ Potential Breakage</h2>
<p>This release updates our version of <code>getrandom</code> to
<code>0.3</code> and <code>rand</code> to <code>0.9</code>. It is a
<strong>potentially breaking change</strong> for the following
users:</p>
<h3>no-std users who enable the <code>rng</code> feature</h3>
<p><code>uuid</code> still uses <code>getrandom</code> by default on
these platforms. Upgrade your version of <code>getrandom</code> and <a
href="https://docs.rs/getrandom/0.3.1/getrandom/index.html#custom-backend">follow
its new docs</a> on configuring a custom backend.</p>
<h3><code>wasm32-unknown-unknown</code> users who enable the
<code>rng</code> feature without the <code>js</code> feature</h3>
<p>Upgrade your version of <code>getrandom</code> and <a
href="https://docs.rs/getrandom/0.3.1/getrandom/index.html#custom-backend">follow
its new docs</a> on configuring a backend.</p>
<p>You'll also need to enable the <code>rng-getrandom</code> or
<code>rng-rand</code> feature of <code>uuid</code> to force it to use
<code>getrandom</code> as its backend:</p>
<pre lang="diff"><code>[dependencies.uuid]
version = "1.13.0"
- features = ["v4"]
+ features = ["v4", "rng-getrandom"]
<p>[dependencies.getrandom]
</tr></table>
</code></pre></p>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="bf5b0b84d2"><code>bf5b0b8</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/807">#807</a> from
uuid-rs/cargo/v1.14.0</li>
<li><a
href="daa07949e9"><code>daa0794</code></a>
prepare for 1.14.0 release</li>
<li><a
href="6bd7bc791b"><code>6bd7bc7</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/806">#806</a> from
tysen/add-fromstr-impls-to-fmt</li>
<li><a
href="5b0ca42c80"><code>5b0ca42</code></a>
Add FromStr impls to the fmt structs</li>
<li><a
href="d8871b3b03"><code>d8871b3</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/805">#805</a> from
uuid-rs/cargo/v1.13.2</li>
<li><a
href="704421094a"><code>7044210</code></a>
prepare for 1.13.2 release</li>
<li><a
href="7893ecce7f"><code>7893ecc</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/804">#804</a> from
uuid-rs/fix/wasm-no-rng</li>
<li><a
href="bf28001d53"><code>bf28001</code></a>
update feature docs</li>
<li><a
href="920e8b183f"><code>920e8b1</code></a>
add a more descriptive compile error when no rng source is available on
wasm</li>
<li><a
href="54214179a6"><code>5421417</code></a>
Merge pull request <a
href="https://redirect.github.com/uuid-rs/uuid/issues/799">#799</a> from
uuid-rs/cargo/1.13.1</li>
<li>Additional commits viewable in <a
href="https://github.com/uuid-rs/uuid/compare/1.11.0...v1.14.0">compare
view</a></li>
</ul>
</details>
<br />
[](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after
your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge
and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating
it. You can achieve the same result by closing it manually
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
In #8159, we introduced a regression that could lead to a deadlock when
shutting down the TUN device. Whilst we did close the channel prior to
awaiting the thread to exit, we failed to notice that _another_ instance
of the sender could be alive as part of an internally stored "sending
permit" with the `PollSender` in case another packet is queued for
sending. We need to explicitly call `abort_send` to free that.
Judging from the comment and a prior bug, this shutdown logic has been
buggy before. To further avoid this deadlock, we introduce two changes:
- The worker threads only receive a `Weak` reference to the
`wintun::Session`
- We move all device-related state into a dedicated `TunState` struct
that we can drop prior to joining the threads
The combination of these features means that all strong references to
channels and the session are definitely dropped without having to wait
for anything. To provide a clean and synchronous shutdown, we wait for
at most 5s on the worker-threads. If they don't exit until then, we log
a warning and exit anyway.
This should greatly reduce the risk of future bugs here because the
session (and thus the WinTUN device) gets shutdown in any case and so at
worst, we have a few zombie threads around.
Resolves: #8265
Currently, it would theoretically be possible for an admin to connect
non-internet Resources to the Internet site. This PR fixes that by
enforcing only the `internet` Resource type can belong to the `Internet`
gateway group.
Related: #6834
`@Published` properties that views subscribe to for UI updates need to
be updated from the main thread only. This PR annotates the relevant
variable and function from the original author's implementation with
`@MainActor` so that Swift will properly warn us when modifying these in
the future.
A regression was introduced in #8218 that removed the `menuBar` as an
environment object for `AppView`.
Unfortunately this compiles just fine, as EnvironmentObjects are loaded
at runtime, causing the "Open Menu" button to crash since it's looking
for a non-existent EnvironmentObject.
Sentry uncovered a bug in the resources index liveview where it looks
like some code copy-pasted from the policies index view wasn't updated
properly to work in the resources live view, causing the view to crash
if an admin was viewing the table while the resources are changed in
another page.
In debugging that, I realized the best UX when viewing these tables is
usually just to show a `Reload` button and not update the data live
while the admin is viewing it, as this can cause missed clicks and other
annoyances.
This PR adds an optional `stale` component attribute that, if true, will
render a `Reload` button in the live table which upon clicking will
reload the live table.
Not all index views are updated with this - in some views there is
already logic to handle making an intelligent update without breaking
the view if the data is updated - for example for the clients table.
Ideally, we live-update things that don't reflow layout inline (such as
`online/offline` presence) and for things that do cause layout reflow
(create/delete), we show the `Reload` button.
However that work is saved for a future PR as this one fixes the
immediate bug and this is not the highest priority.
<img width="1195" alt="Screenshot 2025-02-16 at 8 44 43 AM"
src="https://github.com/user-attachments/assets/114efffa-85ea-490d-9cea-78c607081ce3"
/>
<img width="401" alt="Screenshot 2025-02-16 at 9 59 53 AM"
src="https://github.com/user-attachments/assets/8a570213-d4ec-4b6c-a489-dcd9ad1c351c"
/>
It's possible for a client or admin to try and load the redirect URL
directly, or a misconfigured IdP may redirect back to us with missing
params. We should redirect with an error flash instead of 500'ing.
This configures the GUI client to log to journald in addition to files
as well. For better or worse, this logs all events such that structured
information is preserved, e.g. all additional fields next to the message
are also saved as fields in the journal. By default, when viewing the
logs via `journalctl`, those fields are not displayed. This makes the
default output of `journalctl` for the FIrezone GUI not as useful as it
could be. Fixing that is left to a later stage.
Related: #8173
The original MenuBar developer set a few anti-patterns that were
somewhat followed by subsequent developers. As of now, the entire file
is too large and woefully cluttered.
This PR takes a big step towards #7771 by first organizing the current
thing into a more comprehensible file:
- `private` is removed. These are not needed in Swift unless you
actually need to make something private. The default `internal` level is
appropriate for most cases.
- state change handlers are consistently named `handleX`
- functions are reorganized, and `MARK` comments used to group similar
functions together
Our application state is incredibly simple, only consisting of a handful
of properties.
Throughout our codebase, we use a singular global state store called
`Store`. We then inject this as the singular piece of state into each
view's model.
This is unnecessary boilerplate and leads to lots of duplicated logic.
Instead we refactor away (nearly) all of the application's view models
and instead use an `@EnvironmentObject` to inject the store into each
view.
This convention drastically simplifies state tracking logic and
boilerplate in the views.
Using `rustup` - even on NixOS - is easier to manage the Rust toolchain
as some tools rely on being able to use the `rustup` shims such as
`+nightly` to run a nightly toolchain.
With the addition of the Firezone Control Protocol, we are now issuing a
lot more DNS queries on the Gateway. Specifically, every DNS query for a
DNS resource name always triggers a DNS query on the Gateway. This
ensures that changes to DNS entries for resources are picked up without
having to build any sort of "stale detection" in the Gateway itself. As
a result though, a Gateway has to issue a lot of DNS queries to upstream
resolvers which in 99% or more cases will return the same result.
To reduce the load on these upstream, we cache successful results of DNS
queries for 5 minutes.
---------
Signed-off-by: Thomas Eizinger <thomas@eizinger.io>
On Linux, logs sent to stdout from a systemd-service are automatically
captured by `journald`. This is where most admins expect logs to be and
frankly, doing any kind of debugging of Firezone is much easier if you
can do `journalctl -efu firezone-client-ipc.service` in a terminal and
check what the IPC service is doing.
On Windows, stdout from a service is (unfortunately) ignored.
To achieve this and also allow dynamically changing the log-filter, I
had to introduce a (long-overdue) abstraction over tracing's "reload"
layer that allows us to combine multiple reload-handles into one.
Unfortunately, neither the `reload::Layer` nor the `reload::Handle`
implement `Clone`, which makes this unnecessarily difficult.
Related: #8173
This PR fixes two issues:
1. Since we weren't updating any actual fields in the telemetry reporter
log record, it was never being updated, thus optimistic locking was not
taking effect. To fix this, we use `Repo.update(force: true)`.
2. If a buffer is full, we write immediately, but we provider an empty
`%Log{}` which causes a repetitive `the current value of last_flushed_at
is nil and will not be used as a filter for optimistic locking.`