+2
nixos/doc/manual/release-notes/rl-2505.section.md
+2
nixos/doc/manual/release-notes/rl-2505.section.md
···- [Schroot](https://codeberg.org/shelter/reschroot), a lightweight virtualisation tool. Securely enter a chroot and run a command or login shell. Available as [programs.schroot](#opt-programs.schroot.enable).+- [Firezone](https://firezone.dev), an enterprise-ready zero-trust access platform built on WireGuard. This includes the server stack as [services.firezone.server.enable](#opt-services.firezone.server.enable), a TURN/STUN relay service as [services.firezone.relay.enable](#opt-services.firezone.relay.enable), a gateway service as [services.firezone.gateway.enable](#opt-services.firezone.gateway.enable), a headless client as [services.firezone.headless-client.enable](#opt-services.firezone.headless-client.enable) and a GUI client as [services.firezone.gui-client.enable](#opt-services.firezone.gui-client.enable).- [crab-hole](https://github.com/LuckyTurtleDev/crab-hole), a cross platform Pi-hole clone written in Rust using hickory-dns/trust-dns. Available as [services.crab-hole](#opt-services.crab-hole.enable).
+5
nixos/modules/module-list.nix
+5
nixos/modules/module-list.nix
···
+159
nixos/modules/services/networking/firezone/gateway.nix
+159
nixos/modules/services/networking/firezone/gateway.nix
···+[upstream setup script](https://github.com/firezone/firezone/blob/8c7c0a9e8e33ae790aeb75fdb5a15432c2870b79/scripts/gateway-systemd-install.sh#L154-L168)
+138
nixos/modules/services/networking/firezone/gui-client.nix
+138
nixos/modules/services/networking/firezone/gui-client.nix
···
+148
nixos/modules/services/networking/firezone/headless-client.nix
+148
nixos/modules/services/networking/firezone/headless-client.nix
···
+709
nixos/modules/services/networking/firezone/provision.exs
+709
nixos/modules/services/networking/firezone/provision.exs
···+{:ok, resource} when resource.replaced_by_resource_id != nil -> fetch_resource(resource.replaced_by_resource_id, subject)+{:ok, policy} when policy.replaced_by_policy_id != nil -> fetch_policy(policy.replaced_by_policy_id, subject)+Auth.create_token(temp_admin_actor_email_identity, temp_admin_actor_context, "temporarynonce", DateTime.utc_now() |> DateTime.add(1, :hour))+{temp_admin_subject, temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token}+defp cleanup_temp_admin(temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token, subject) do+{:ok, actor_group} = Actors.create_managed_group(account, %{name: "Everyone", membership_rules: [%{operator: true}]})+multi = Enum.reduce(account_data["actors"] || %{}, multi, fn {external_id, actor_data}, multi ->+case uuid && Actors.fetch_actor_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do+multi = Enum.reduce(account_data["auth"] || %{}, multi, fn {external_id, provider_data}, multi ->+case uuid && Auth.fetch_provider_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do+multi = Enum.reduce(account_data["gatewayGroups"] || %{}, multi, fn {external_id, gateway_group_data}, multi ->+case uuid && Gateways.fetch_group_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do+multi = Enum.reduce(account_data["relayGroups"] || %{}, multi, fn {external_id, relay_group_data}, multi ->+multi = Enum.reduce(account_data["groups"] || %{}, multi, fn {external_id, actor_group_data}, multi ->+case uuid && Actors.fetch_group_by_id(uuid, temp_admin_subject) |> nil_if_deleted_or_not_found() do+multi = Enum.reduce(account_data["resources"] || %{}, multi, fn {external_id, resource_data}, multi ->+:connections -> value == Enum.map(existing.connections || [], fn conn -> Map.take(conn, [:gateway_group_id]) end)+:filters -> value == Enum.map(existing.filters || [], fn filter -> Map.take(filter, [:ports, :protocol]) end)+multi = Enum.reduce(account_data["policies"] || %{}, multi, fn {external_id, policy_data}, multi ->+{temp_admin_subject, temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token} =+cleanup_temp_admin(temp_admin_actor, temp_admin_actor_email_identity, temp_admin_actor_token, temp_admin_subject)+Logger.error("Provisioning failed at step #{inspect(step)}, no changes were applied: #{inspect(reason)}")
+202
nixos/modules/services/networking/firezone/relay.nix
+202
nixos/modules/services/networking/firezone/relay.nix
···+message = "At least one of `services.firezone.relay.publicIpv4` and `services.firezone.relay.publicIpv6` must be set";
+1210
nixos/modules/services/networking/firezone/server.nix
+1210
nixos/modules/services/networking/firezone/server.nix
···+adapter_config.client_secret = "{env:AUTH_CLIENT_SECRET_${toUpper accountName}_${toUpper authName}}";+list of available variables, please refer to the [upstream definitions](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/config/definitions.ex).+variables, please refer to the [upstream definitions](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/config/definitions.ex).+The Firezone documentation holds [a list of supported Swoosh adapters](https://github.com/firezone/firezone/blob/main/website/src/app/docs/reference/env-vars/readme.mdx#outbound-emails).+description = "File containing the password for the given username. Beware that a file in the nix store will be world readable.";+description = "The address of this resource. Depending on the resource type, this should be an ip, ip with cidr mask or a domain.";+description = "An optional description for resource address, usually a full link to the resource including a schema.";+description = "A list of gateway groups (sites) which can reach the resource and may be used to connect to it.";+xs: map (x: if x.from == x.to then toString x.from else "${toString x.from} - ${toString x.to}") xs;+description = "A list of filter to restrict traffic. If no filters are given, all traffic is allowed.";+description = "Ensure that only the given members are part of this group at every server start.";+Please refer to the [Firezone source code](https://github.com/firezone/firezone/blob/main/elixir/apps/domain/lib/domain/accounts/account.ex)+message = "An account name must contain only lowercase characters and underscores, as it will be used as the URL slug for this account.";+message = "The authentication provider attribute key must contain only letters, numbers, underscores or dashes.";+urlComponents = builtins.elemAt (builtins.split "https://([^/]*)(/?.*)" cfg.web.externalUrl) 1;+urlComponents = builtins.elemAt (builtins.split "https://([^/]*)(/?.*)" cfg.api.externalUrl) 1;+while [[ "$(curl -s "http://localhost:${toString cfg.domain.settings.HEALTHZ_PORT}" 2>/dev/null || echo)" != '{"status":"ok"}' ]]
+1
nixos/tests/all-tests.nix
+1
nixos/tests/all-tests.nix
···
+86
nixos/tests/firezone/create-tokens.exs
+86
nixos/tests/firezone/create-tokens.exs
···+Auth.create_token(temp_admin_actor_email_identity, temp_admin_actor_context, "temporarynonce", DateTime.utc_now() |> DateTime.add(1, :hour))
+349
nixos/tests/firezone/firezone.nix
+349
nixos/tests/firezone/firezone.nix
···+${lib.getExe config.services.firezone.server.domain.package} rpc 'Code.eval_file("${./create-tokens.exs}")'+relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Connected to portal.*${domain}'", timeout=30)+gateway.wait_until_succeeds("journalctl --since -2m --unit firezone-gateway.service --grep 'Connected to portal.*${domain}'", timeout=30)+relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Created allocation.*IPv4'", timeout=30)+relay.wait_until_succeeds("journalctl --since -2m --unit firezone-relay.service --grep 'Created allocation.*IPv6'", timeout=30)+gateway.wait_until_succeeds("journalctl --since -2m --unit firezone-gateway.service --grep 'Updated allocation.*relay_ip4.*Some.*relay_ip6.*Some'", timeout=30)+client.wait_until_succeeds("journalctl --since -2m --unit firezone-headless-client.service --grep 'Connected to portal.*${domain}'", timeout=30)+client.wait_until_succeeds("journalctl --since -2m --unit firezone-headless-client.service --grep 'Tunnel ready'", timeout=30)+client.wait_until_succeeds("curl -4 -Lsf http://resource.example.com | grep 'greetings from the resource'")+client.wait_until_succeeds("curl -6 -Lsf http://resource.example.com | grep 'greetings from the resource'")