Firezone

firezone.dev
Firezone

Open-source self-hosted VPN and firewall built on WireGuard®.

Open Source

Firezone Source Code

Author

firezone

Description

Enterprise-ready zero-trust access platform built on WireGuard®.

#cloud#devsecops#elixir#elixir-lang#firewall#liveview#network#network-security#networking#phoenix#privacy#rust-lang#security#self-hosted#virtual-network#vpn#vpn-server#wireguard#wireguard-ui#wireguard-vpn

Homepage

https://www.firezone.dev

License

Apache-2.0

Created

22 Apr 20

Last Updated

29 Jul 26

Latest version

macos-client-1.5.18

Primary Language

Elixir

Size

233,613 KB

Stars

8,972

Forks

438

Watchers

8,972

Language Usage

Language Usage

Star History

Star History

Recent Commits

  • Jamil (29 Jul 26)

    refactor(gui-client): clean up frontend implementation (#14403) ## Summary - remove the now-unused Flowbite, Heroicons, Source Sans, and `vite-plugin-typescript` dependencies - remove obsolete Flowbite theme/build configuration and isolate the code-only component refactors from #14397 - eliminate the Vite TS5096 warning and configure ESLint React version detection ## Stack - Depends on #14397; this PR contains the refactoring and tooling follow-up requested in review. - Import, declaration, and JSX prop ordering changes are deliberate. Prettier does not manage ordering in this repository, and no sorting lint rule or plugin is configured. ## Testing - `pnpm install --frozen-lockfile` - `pnpm exec vite build` - `pnpm exec eslint src-frontend` - `mise run lint` - Nix frontend derivation build

  • Thomas Eizinger (29 Jul 26)

    feat(connlib): enable URO on Windows until proven broken (#14392) URO is the only receive-path batching available on Windows but has been off since `quinn-udp` disabled it by default: some machines coalesce UDP datagrams without attaching the `UDP_COALESCED_INFO` metadata needed to split them apart again, most notably Windows on ARM with WSL2 installed and certain dock NIC drivers on Windows 11 24H2. Nobody can identify the affected machines up front, but we do not have to: correct coalescing reports the original datagram size as the segment size, so a receive that violates that proves the machine's coalescing is broken. We opt every socket into URO and detect broken coalescing in two ways: 1. a segment larger than `MAX_FZ_PAYLOAD` (no Firezone peer sends a datagram that big) 2. a receive reported as a single datagram whose payload is provably a train of several Firezone datagrams. The latter works because everything our sockets receive leaves the datagram boundaries recoverable: WireGuard handshake messages have fixed sizes, WireGuard data packets have a recognizable header, and STUN / TURN channel-data messages carry their own length. On detection we opt out of URO for the remainder of the process and salvage the offending receive by reporting the recovered segment size where possible; unsalvageable receives are dropped and the tunneled protocols retransmit their contents. On healthy machines, URO batches up to ~49 datagrams per `WSARecvMsg` call. Related: https://github.com/quinn-rs/quinn/issues/2041 --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (29 Jul 26)

    feat(windows): coalesce TCP packets before writing to WinTUN (#14404) On Windows, download (Gateway -> Client) throughput performance currently isn't great for two reasons: 1. The WinTUN driver doesn't expose any offloading features, so each IP packet has to be copied to and from the kernel individually. 2. URO on Windows is buggy on some network drivers, so offloading on the UDP socket is also only enabled for the upload path. This PR presents a fairly targeted fix for (1) but only for TCP and only for the download path. As local benchmarking shows, the Windows network stack does basically no work from the moment it hands us an IP packet and therefore the upload path is already very fast (saturating the 2.5GBit/s link in the benchmarking rig). This makes sense: In the upload path, Firezone sits on the receiving end of the IP traffic. All we need to do is encrypt and send it over UDP as fast as possible. In the download path, Firezone is the first link of the chain that sees the IP packet and Windows' own processing of IP packets that we pass to it follow from that. As profiling shows, Windows spends a fair amount of time processing each IP packet. Unfortunately, the WinTUN driver does not offer any way of passing a batch of IP packets to it, similar to how Linux offers GSO. But what it does support is IP packets with a size of up to 65535 bytes. This turns out to be very useful. This PR extract the packet coalescing which we already use to perform GSO on Linux into a shared crate and re-uses it on Windows to only coalesce TCP packets. Essentially, we are assembling a large TCP segment with all its headers, options and checksums correctly computed and passing it in one copy to WinTUN. This amortizes at least some of the per-packet processing cost. For UDP, we cannot do this unfortunately so coalescing is disabled there. In order to make this safer to rollout, it is gated behind the `wintun-tcp-coalescing` feature flag, defaulting to false. With the feature flag enabled, this change enables a more than 2x win in throughput in the download path on Windows.

  • Thomas Eizinger (29 Jul 26)

    test(connlib): widen bootstrap_doh window to 10s (#14402) The `bootstrap_doh` test resolves `cloudflare-dns.com` and completes a TLS handshake against the real DoH server within a fixed window. A 2s window is too tight on slow macOS CI runners, where the handshake can outlast it and the second query re-returns the bootstrapping placeholder, panicking on `unwrap`. Widening the window to 10s comfortably covers a real resolution plus handshake while keeping the test pointed at a real DoH server. Fixes: #14401 Co-authored-by: Claude <[email protected]>

  • Jamil (29 Jul 26)

    feat(gui-client): match portal styling (#14397) ## Summary - restyle the Tauri GUI client to match the portal’s fonts, colors, spacing, iconography, and ligatures - replace UI elements with theme-specific primitives while preserving the existing copy and behavior - update the frontend dependency hash and make pnpm non-interactive in the Nix build ## Stack - Styling only. Refactoring and legacy frontend-tool cleanup moved to #14403. --- Fixes #14380

  • Jamil (28 Jul 26)

    fix(portal): fix race in replication slot poll (#14400)

  • Jamil (28 Jul 26)

    chore(portal): exempt global features join from credo check (#14399) The trust-anchor query joins the features table, which is global per-deployment state with no account_id column, so the account-scoping credo check added in #14398 flags it. #14291 merged after that check landed, so the warning reached main unnoticed since credo is not in CI. This adds the documented suppression comment to the join. Related: #14291, #14398

  • Jamil (28 Jul 26)

    feat(portal): add device-trust challenge verification (#14291) Adds the module that decides whether a device-trust challenge response can be trusted. The client sends one or more certificates plus a signature over a nonce. An entry is trusted only when the leaf allows client authentication, is within its validity window, chains to one of the account's uploaded trust anchors, and the signature verifies against the leaf's key. Trusted leaves yield device identifiers parsed from firezone:// URI SANs, with fallbacks for common MDM conventions. Values are normalized and screened against well-known garbage (OEM placeholder serials, all-zero UUIDs) before they can ever reach an indexed column. Nothing calls this yet. Related: #14290 #14254 --------- Co-authored-by: Claude Fable 5 <[email protected]>

  • Jamil (28 Jul 26)

    fix(portal): scope joined rows by account (#14398) Safe.scoped/2 applies the account predicate to the root query binding, but it does not automatically scope every joined table. Because tenant-owned tables use composite `(account_id, id)` keys, joining only on an ID can associate a row from another account when UUIDs collide. This PR: - adds matching `account_id` predicates to tenant-owned joins across Portal, PortalAPI, and PortalWeb - makes group member/policy aggregate subqueries group and join by both `account_id` and `group_id` - adds `Credo.Check.Warning.MissingAccountIdInJoin` to prevent future omissions in piped and keyword Ecto joins - exempts account-table joins and documents targeted suppressions for two derived one-row CTE joins - adds a regression test proving an API client does not join a token from another account with the same actor UUID Testing: - focused affected test suite: 696 tests, 0 failures - `test/portal_web/live/settings/api_clients/index_test.exs`: 15 tests, 0 failures - custom Credo checks: 20 tests, 0 failures - new Credo rule across 476 application source files: 0 issues - `mix credo --strict` - `mix format --check-formatted` - `git diff --check` ---

  • Thomas Eizinger (28 Jul 26)

    perf(connlib): size the WinTUN ring buffer for 10 Gbit/s (#14396) The WinTUN ring buffer capacity was a hand-picked 1 MiB, which holds only 0.8 ms of traffic at the 10 Gbit/s the UDP socket buffers are sized for. A brief scheduling delay on either TUN worker thread is enough to overflow it and drop packets. Derive it the same way instead: the highest rate we expect to move, for as long as a normally-scheduled thread may go without servicing the ring. That lands on 16 MiB per ring, which also exceeds what the outbound TUN channel can hand to the send ring while the Windows network stack is not draining it. --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    fix(connlib): round up the packet count in the GSO send trace (#14394) Counting the datagrams in a GSO batch has to round up, because the last segment may be shorter than `segment_size`. The `wire::net::send` trace floored instead, so it under-reported by one whenever a batch ended in a short segment, and logged zero for a chunk carrying a single short datagram. Everywhere else that derives this count already rounds up. Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    fix(connlib): count GRO segments in the UDP recv batch metric (#14393) The UDP receive path recorded how many buffers a batched `recv` filled, not how many datagrams it delivered. With GRO on Linux a single buffer holds several datagrams, and on Windows the buffer count is always one no matter how many segments URO packed into it, so the receive side of `connlib.network.packets.batch_count` read well below reality and could not be compared against the transmit side. The histogram's buckets also stopped at 31, while one `recvmmsg` can deliver 2048 datagrams and the TUN paths batch up to 100 per syscall, so the range is widened. Its unit and description claimed to count batches when every call site records packets. --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    refactor(connlib): move `TunnelError` into the tunnel crate (#14391) `TunnelError` collects the errors that occur while the event-loop drives IO. The sans-IO state machines never emit one, which is why the fuzz harness had to match the event variant with `unreachable!`. Moving the type into `tunnel` and surfacing it as the error half of what `poll_next_event` returns leaves `tunnel-proto` exposing just the state-machine API, and stops dead error-handling code from counting against the fuzz coverage of the sans-IO crate. --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    fix(gui-client): hide console windows during MSI install (#14389) `register-sparse.exe` is a console-subsystem binary, so Windows allocates a console for it on every `CreateProcess`. That means the sparse-MSIX custom actions flash terminal windows at the user during install and uninstall, which looks like something went wrong. Release builds now use the Windows subsystem so no console is ever allocated; debug builds keep theirs, since that is where the binary actually gets run by hand. Nothing is lost on the install path: MSI discards stdout from deferred EXE custom actions anyway, and the log file plus Sentry remain the sources of truth. Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    ci(rust): report fuzz coverage only for our own sources (#14390) Fuzz builds instrument the entire dependency tree, including a from-source standard library. Exporting that profile verbatim meant Coveralls counted registry crates, git checkouts and the standard library as project code, which diluted the reported number to a small fraction of the real one. Replaying the committed `tunnel-proto` corpus, the export goes from 2750 files at 10.24% to 147 files at 69.46%, the latter being exactly the `rust/` subtree Coveralls already shows separately. --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    docs(rust): record why release builds pin codegen-units (#14388) The existing comment framed `codegen-units = 1` as buying smaller code in exchange for compile time. Benchmarking the release profile showed that is not what it does here: under `lto = "fat"` the setting costs nothing in build time and is what keeps peak compiler memory and binary size down, and the eBPF TURN router does not link without it at all. Recording the measurements next to the setting so the next person to reach for it knows what it is holding up. Co-authored-by: Claude <[email protected]>

  • dependabot[bot] (28 Jul 26)

    build(deps): bump the rust-crypto group (#14184) Updates `hmac` from 0.12.1 to 0.13.0 Updates `sha2` from 0.10.9 to 0.11.0 --------- Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    ci(nix): build each package on its own runner (#14386) The three Nix derivations each compile the whole Rust dependency graph. Sharing a single 4-vCPU runner leaves them contending for its cores, which makes the Nix job the longest in CI at roughly 21 minutes and puts it on the critical path for both PRs and the merge queue. Building each package on its own matrix leg trades runner minutes, which are free on public repos, for wall-clock time. The `firezone-gui-client` leg becomes the new critical path at an estimated 12 to 13 minutes, since it carries both the most crates and the longest link-time-optimization tail. Compiling the shared dependency graph once instead of three times is the larger win and is left for a follow-up. --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    ci: skip the disk-space reclaim when the runner has room (#14387) The hosted Linux runners now leave far more room than this action reclaims. Sampling the four labels we use showed 88 GB free on `ubuntu-24.04` and `ubuntu-latest`, 87 GB on `ubuntu-22.04` and 109 GB on `ubuntu-24.04-arm`, against a reclaim that frees roughly 21 GB. Deleting those toolchains costs 40-80s per job for headroom we already have. The reclaim now runs only when the root filesystem is below `required-gb`, so it stays available if the images ever shrink again rather than being dropped outright. Runner disk size is undocumented and has been reduced without notice before, so this keeps the safety net at no cost. Related: #13508 Related: #14309 Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    ci(fuzz): install cargo-fuzz from GitHub releases (#14385) The `cargo:` backend has no prebuilt artefacts to fall back on, so every mise cache miss compiles `cargo-fuzz` from source and the lockfile can only pin a version. Upstream publishes prebuilt binaries, so we now fetch those and pin them by checksum like the rest of our Rust tooling. Note that upstream only builds x86_64, so `arm64` Linux no longer gets `cargo-fuzz` from mise. --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    fix(connlib): exit on unrecoverable UDP bind failures (#14369) Losing every UDP socket used to suspend the event-loop indefinitely behind a single log line, leaving a session that looked connected but carried no traffic until a network change happened to rebind it. Bind failures now travel the same error channel as runtime socket errors, so the event-loop sees them. Whether one is worth dying over is the socket factory's call rather than something `Sockets` can infer: a family that won't bind is survivable, but Android's `protect` callback failing means every socket we make would route back into our own tunnel. Factories mark the latter, and both event-loops shut down loudly when they see it. Related: #14363 --------- Co-authored-by: Claude <[email protected]>

  • Thomas Eizinger (28 Jul 26)

    test(connlib): replace tunnel proptests with a fuzzer (#13935) This replaces the tunnel state-machine proptest suite and harvester with a coverage-guided libFuzzer target. The committed corpus is both the seed corpus and deterministic regression suite; CI replays every input and pins `tunnel-proto` region coverage in `tests/fuzz/expected-coverage/tunnel-proto.json`. Instead of sampling input values at random, the entire state plus transitions are deterministically derived via `arbitrary::Unstructured` from the fuzzer's byte stream. This allows the fuzzer to make targeted changes to the scenario by modifying individual bits in the input stream. Many proptest-isms can be deleted as a result of this move and much of the test-input generation code is now actually much easier to read. Finally, PRs will only ever replay the current corpus but never fuzz itself. The `tunnel-test` job is gone. It is being replace by `rust / fuzz-tunnel-proto`. It can only fail for one of two reasons: - A previously passing behaviour is no longer passing - The coverage dropped Resolves: #14244 --------- Co-authored-by: Claude <[email protected]>

  • Jamil (27 Jul 26)

    feat(portal): add X509 device-trust helpers (#14290) Adds certificate parsing helpers to Portal.Crypto.X509 for the device-trust work: reading URI and DNS subject alternative names, checking the client-authentication EKU, extracting the subject public key and the matching digest for signature verification, and reading subject OU values. Nothing calls these yet. Related: #14254 --------- Co-authored-by: Claude Fable 5 <[email protected]>

  • dependabot[bot] (27 Jul 26)

    build(deps-dev): bump lazy_html from 0.1.11 to 0.1.12 in /elixir (#14316) Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • dependabot[bot] (27 Jul 26)

    build(deps): bump @fontsource-variable/roboto from 5.2.10 to 5.3.0 in /elixir/assets (#14355) Bumps [@fontsource-variable/roboto](https://github.com/fontsource/font-files/tree/HEAD/fonts/variable/roboto) from 5.2.10 to 5.3.0. <details> <summary>Commits</summary> <ul> <li>See full diff in <a href="https://github.com/fontsource/font-files/commits/HEAD/fonts/variable/roboto">compare view</a></li> </ul> </details> <br /> Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • dependabot[bot] (27 Jul 26)

    build(deps): bump the com-android group (#14376) Updates `com.android.application` from 9.3.0 to 9.3.1 Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • dependabot[bot] (27 Jul 26)

    build(deps): bump the codeql group (#14373) Updates `github/codeql-action/upload-sarif` from 4.35.2 to 4.37.3 Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • dependabot[bot] (27 Jul 26)

    build(deps): bump actions/cache/restore from 5.0.5 to 6.1.0 in /.github/actions/setup-rust-binary-cache (#14375) Signed-off-by: dependabot[bot] <[email protected]> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

  • Jamil (27 Jul 26)

    fix(portal): keep connection state on device row updates (#14381) When a row changes in the database, the portal tells every connected client and gateway so they can update their copy of it. Each connection also holds things in memory that belong to that connection alone, most importantly the WireGuard public key the device sent when it connected. That key only reaches the database a few seconds later, written by a batched job. The update replaced the connection's whole in-memory copy with the database row. When it arrived before the batched write landed, the connection lost its public key, along with the other connect-time details, its virtual fields, and its preloaded associations. Gateways then sent `flow_created` with a null `gateway_public_key`, and clients threw the message away and never connected. The update now keeps everything the connection owns and takes only the columns that actually changed. The rule lives next to the code that builds these structs from the database log, since that is what drops the in-memory parts, so it is one shared helper rather than a per-handler list. The account handlers use it too; they had the same hole, just nothing reading through it yet. Fixes #14382 --------- Co-authored-by: Claude Opus 5 (1M context) <[email protected]>

  • Thomas Eizinger (27 Jul 26)

    fix(anyhow-ext): find custom errors inside `io::Error` (#14370) `io::Error` reports the source of its custom error rather than that error itself, so a typed error boxed into one is invisible to anything walking `Error::source`. Callers had to know that and reach for `get_ref` by hand, which is exactly the sharp edge `any_is` and `any_downcast_ref` exist to hide. Related: #14369 --------- Co-authored-by: Claude <[email protected]>

Firezone Website

Website

Zero Trust Access That Scales | Firezone

Replace your VPN with Firezone, an open-source zero trust access platform built on WireGuard®. Connect users to anything, anywhere. Try free today.

Redirects

Does not redirect

Security Checks

2 security checks failed (63 passed)

  • Domain Recently Created
  • Domain Very Recently Created

Server Details

  • IP Address 66.33.60.129
  • Location Walnut, California, United States of America, NA
  • ISP Vercel Inc
  • ASN AS16509

Associated Countries

  • US US
  • CA CA

Safety Score

Website marked as safe

100%

Blacklist Check

www.firezone.dev was found on 0 blacklists

  • AntiSocial Blacklist
  • Artists Against 419
  • Badbitcoin
  • Bambenek Consulting
  • CERT Polska
  • CoinBlockerLists
  • CRDF
  • CryptoScamDB
  • EtherAddressLookup
  • EtherScamDB
  • Fake Website Buster
  • MetaMask EthPhishing
  • NABP Not Recommended Sites
  • OpenPhish
  • PetScams
  • PhishFeed
  • PhishFort
  • Phishing.Database
  • PhishStats
  • PhishTank
  • Phishunt
  • RPiList Not Serious
  • Scam.Directory
  • SecureReload Phishing List
  • Spam404
  • StopGunScams
  • Suspicious Hosting IP
  • ThreatFox
  • ThreatLog
  • TweetFeed
  • URLhaus
  • ViriBack C2 Tracker

Website Preview

Website preview

Firezone Reviews

More Self-Hosted Network Security

About the Data: Firezone

API

You can access Firezone's data programmatically via our API. Simply make a GET request to:

https://api.awesome-privacy.xyz/v1/services/firezone

The REST API is free, no-auth and CORS-enabled. To learn more, view the API Docs or read the API Usage Guide.

Share Firezone

Help your friends compare Self-Hosted Network Security, and pick privacy-respecting software and services.
Share Firezone and Awesome Privacy with your network!

View Self-Hosted Network Security (8)