wip: more firezone clients

This commit is contained in:
oddlama 2025-02-07 03:44:29 +01:00
parent bbb76ae5ec
commit e918a6d581
No known key found for this signature in database
GPG key ID: 14EFE510775FE39A
14 changed files with 1174 additions and 141 deletions

View file

@ -8,6 +8,8 @@
./backups.nix ./backups.nix
./deterministic-ids.nix ./deterministic-ids.nix
./distributed-config.nix ./distributed-config.nix
./firezone-relay.nix
./firezone-gateways.nix
./firezone-server.nix ./firezone-server.nix
./globals.nix ./globals.nix
./meta.nix ./meta.nix

View file

@ -0,0 +1,170 @@
{
lib,
pkgs,
config,
...
}:
let
inherit (lib)
boolToString
concatMapAttrs
flip
getExe
mkEnableOption
mkOption
mkPackageOption
types
;
in
{
options = {
services.firezone.gateways = mkOption {
description = ''
A set of gateway clients to deploy on this machine. Each gateway can
connect to exactly one firezone server.
'';
default = { };
type = types.attrsOf (
types.submodule (gatewaysSubmod: {
options = {
package = mkPackageOption pkgs "firezone-gateway" { };
name = mkOption {
type = types.str;
default = gatewaysSubmod.config._module.args.name;
description = "The name of this gateway as shown in firezone";
};
user = mkOption {
type = types.strMatching "^[a-zA-Z0-9_-]{1,32}$";
default = "firezone-gw-${gatewaysSubmod.config._module.args.name}";
description = "The DynamicUser name under which the gateway will run. Cannot exceed 32 characters.";
};
interface = mkOption {
type = types.strMatching "^[a-zA-Z0-9_-]{1,15}$";
default = "tun-${gatewaysSubmod.config._module.args.name}";
description = "The name of the TUN interface which will be created by this gateway";
};
apiUrl = mkOption {
type = types.str;
example = "wss://firezone.example.com/api";
description = ''
The URL of your firezone server's API. This should be the same
as your server's setting for {option}`services.firezone.server.settings.api.externalUrl`,
but with `wss://` instead of `https://`.
'';
};
tokenFile = mkOption {
type = types.path;
example = "/run/secrets/firezone-gateway-token";
description = ''
A file containing the firezone gateway token. Do not use a nix-store path here
as it will make the token publicly readable!
This file will be passed via systemd credentials, it should only be accessible
by the root user.
'';
};
logLevel = mkOption {
type = types.str;
default = "info";
description = ''
The log level for the firezone application. See
[RUST_LOG](https://docs.rs/env_logger/latest/env_logger/#enabling-logging)
for the format.
'';
};
enableTelemetry = mkEnableOption "telemetry";
};
})
);
};
};
config = {
systemd.services = flip concatMapAttrs config.services.firezone.gateways (
gatewayName: gatewayCfg: {
"firezone-gateway-${gatewayName}" = {
description = "Gateway service for the Firezone zero-trust access platform";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.util-linux ];
script = ''
# If FIREZONE_ID is not given by the user, use a persisted (or newly generated) uuid.
if [[ -z "''${FIREZONE_ID:-}" ]]; then
if [[ ! -e gateway_id ]]; then
uuidgen -r > gateway_id
fi
export FIREZONE_ID=$(< gateway_id)
fi
export FIREZONE_TOKEN=$(< "$CREDENTIALS_DIRECTORY/firezone-token")
exec ${getExe gatewayCfg.package}
'';
environment = {
FIREZONE_API_URL = gatewayCfg.apiUrl;
FIREZONE_NAME = gatewayCfg.name;
FIREZONE_NO_TELEMETRY = boolToString gatewayCfg.enableTelemetry;
FIREZONE_TUN_INTERFACE = gatewayCfg.interface;
RUST_LOG = gatewayCfg.logLevel;
};
serviceConfig = {
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = false;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_NETLINK"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = "@system-service";
UMask = "077";
Type = "exec";
DynamicUser = true;
User = gatewayCfg.user;
LoadCredential = [ "firezone-token:${gatewayCfg.tokenFile}" ];
DeviceAllow = "/dev/net/tun";
AmbientCapabilities = [ "CAP_NET_ADMIN" ];
CapabilityBoundingSet = [ "CAP_NET_ADMIN" ];
StateDirectory = "firezone-gateways/${gatewayName}";
WorkingDirectory = "/var/lib/firezone-gateways/${gatewayName}";
};
};
}
);
};
meta.maintainers = with lib.maintainers; [
oddlama
patrickdag
];
}

177
modules/firezone-relay.nix Normal file
View file

@ -0,0 +1,177 @@
{
lib,
pkgs,
config,
...
}:
let
inherit (lib)
boolToString
getExe
mkEnableOption
mkIf
mkOption
mkPackageOption
types
;
cfg = config.services.firezone.relay;
in
{
options = {
services.firezone.relay = {
enable = mkEnableOption "the firezone relay server";
package = mkPackageOption pkgs "firezone-relay" { };
publicIpv4 = mkOption {
type = types.str;
description = "The public ipv4 address of this relay";
};
publicIpv6 = mkOption {
type = types.str;
description = "The public ipv6 address of this relay";
};
openFirewall = mkOption {
type = types.bool;
default = true;
description = "Opens up the main STUN port and the TURN allocation range.";
};
port = mkOption {
type = types.port;
default = 3478;
description = "The port to listen on for STUN messages";
};
lowestPort = mkOption {
type = types.port;
default = 49152;
description = "The lowest port to use in TURN allocation";
};
highestPort = mkOption {
type = types.port;
default = 65535;
description = "The highest port to use in TURN allocation";
};
apiUrl = mkOption {
type = types.str;
example = "wss://firezone.example.com/api";
description = ''
The URL of your firezone server's API. This should be the same
as your server's setting for {option}`services.firezone.server.settings.api.externalUrl`,
but with `wss://` instead of `https://`.
'';
};
tokenFile = mkOption {
type = types.path;
example = "/run/secrets/firezone-relay-token";
description = ''
A file containing the firezone relay token. Do not use a nix-store path here
as it will make the token publicly readable!
This file will be passed via systemd credentials, it should only be accessible
by the root user.
'';
};
logLevel = mkOption {
type = types.str;
default = "firezone_relay=info,firezone_tunnel=info,connlib_shared=info,tunnel_state=info,phoenix_channel=info,snownet=info,str0m=info,warn";
description = ''
The log level for the firezone application. See
[RUST_LOG](https://docs.rs/env_logger/latest/env_logger/#enabling-logging)
for the format.
'';
};
enableTelemetry = mkEnableOption "telemetry";
};
};
config = mkIf cfg.enable {
systemd.services."firezone-relay" = {
description = "relay service for the Firezone zero-trust access platform";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
path = [ pkgs.util-linux ];
script = ''
# If FIREZONE_ID is not given by the user, use a persisted (or newly generated) uuid.
if [[ -z "''${FIREZONE_ID:-}" ]]; then
if [[ ! -e relay_id ]]; then
uuidgen -r > relay_id
fi
export FIREZONE_ID=$(< relay_id)
fi
export FIREZONE_TOKEN=$(< "$CREDENTIALS_DIRECTORY/firezone-token")
exec ${getExe cfg.package}
'';
environment = {
FIREZONE_API_URL = cfg.apiUrl;
FIREZONE_NAME = cfg.name;
FIREZONE_TELEMETRY = boolToString cfg.enableTelemetry;
PUBLIC_IPV4_ADDRESS = cfg.publicIpv4;
PUBLIC_IPV6_ADDRESS = cfg.publicIpv6;
LISTEN_PORT = toString cfg.port;
LOWEST_PORT = toString cfg.lowestPort;
HIGHEST_PORT = toString cfg.highestPort;
RUST_LOG = cfg.logLevel;
LOG_FORMAT = "human";
};
serviceConfig = {
LockPersonality = true;
MemoryDenyWriteExecute = true;
NoNewPrivileges = true;
PrivateMounts = true;
PrivateTmp = true;
PrivateUsers = false;
ProcSubset = "pid";
ProtectClock = true;
ProtectControlGroups = true;
ProtectHome = true;
ProtectHostname = true;
ProtectKernelLogs = true;
ProtectKernelModules = true;
ProtectKernelTunables = true;
ProtectProc = "invisible";
ProtectSystem = "strict";
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
"AF_NETLINK"
"AF_UNIX"
];
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
SystemCallArchitectures = "native";
SystemCallFilter = "@system-service";
UMask = "077";
Type = "exec";
DynamicUser = true;
User = "firezone-relay";
LoadCredential = [ "firezone-token:${cfg.tokenFile}" ];
StateDirectory = "firezone-relay";
WorkingDirectory = "/var/lib/firezone-relay";
};
};
};
meta.maintainers = with lib.maintainers; [
oddlama
patrickdag
];
}

View file

@ -85,14 +85,15 @@ let
); );
in in
concatLines ( concatLines (
forEach relevantSecrets (secret: '' forEach relevantSecrets (
export ${secret}=$(< ${ secret:
''export ${secret}=$(< ${
if cfg.settingsSecret.${secret} == null then if cfg.settingsSecret.${secret} == null then
"secrets/${secret}" "secrets/${secret}"
else else
"\"$CREDENTIALS_DIRECTORY/${secret}\"" "\"$CREDENTIALS_DIRECTORY/${secret}\""
}) })''
'') )
); );
provisionStateJson = provisionStateJson =
@ -121,20 +122,20 @@ let
commonServiceConfig = { commonServiceConfig = {
AmbientCapablities = [ ]; AmbientCapablities = [ ];
CapabilityBoundingSet = [ ]; CapabilityBoundingSet = [ ];
LockPersonality = "true"; LockPersonality = true;
MemoryDenyWriteExecute = "true"; MemoryDenyWriteExecute = true;
NoNewPrivileges = "true"; NoNewPrivileges = true;
PrivateMounts = "true"; PrivateMounts = true;
PrivateTmp = "true"; PrivateTmp = true;
PrivateUsers = "false"; PrivateUsers = false;
ProcSubset = "pid"; ProcSubset = "pid";
ProtectClock = "true"; ProtectClock = true;
ProtectControlGroups = "true"; ProtectControlGroups = true;
ProtectHome = "true"; ProtectHome = true;
ProtectHostname = "true"; ProtectHostname = true;
ProtectKernelLogs = "true"; ProtectKernelLogs = true;
ProtectKernelModules = "true"; ProtectKernelModules = true;
ProtectKernelTunables = "true"; ProtectKernelTunables = true;
ProtectProc = "invisible"; ProtectProc = "invisible";
ProtectSystem = "strict"; ProtectSystem = "strict";
RestrictAddressFamilies = [ RestrictAddressFamilies = [
@ -143,9 +144,9 @@ let
"AF_NETLINK" "AF_NETLINK"
"AF_UNIX" "AF_UNIX"
]; ];
RestrictNamespaces = "true"; RestrictNamespaces = true;
RestrictRealtime = "true"; RestrictRealtime = true;
RestrictSUIDSGID = "true"; RestrictSUIDSGID = true;
SystemCallArchitectures = "native"; SystemCallArchitectures = "native";
SystemCallFilter = "@system-service"; SystemCallFilter = "@system-service";
UMask = "077"; UMask = "077";
@ -157,10 +158,12 @@ let
StateDirectory = "firezone"; StateDirectory = "firezone";
WorkingDirectory = "/var/lib/firezone"; WorkingDirectory = "/var/lib/firezone";
Type = "exec";
LoadCredential = mapAttrsToList (secretName: secretFile: "${secretName}:${secretFile}") ( LoadCredential = mapAttrsToList (secretName: secretFile: "${secretName}:${secretFile}") (
filterAttrs (_: v: v != null) cfg.settingsSecret filterAttrs (_: v: v != null) cfg.settingsSecret
); );
Type = "exec";
Restart = "on-failure";
RestartSec = 10;
}; };
componentOptions = component: { componentOptions = component: {
@ -199,20 +202,7 @@ in
options.services.firezone.server = { options.services.firezone.server = {
enable = mkEnableOption "all Firezone components"; enable = mkEnableOption "all Firezone components";
enableLocalDB = mkEnableOption "a local postgresql database for Firezone"; enableLocalDB = mkEnableOption "a local postgresql database for Firezone";
nginx.enable = mkEnableOption "nginx virtualhost definition";
nginx = {
enable = mkEnableOption "nginx virtualhost definition";
apiDomain = mkOption {
type = types.str;
example = "api.firezone.example.com";
description = "The virtual host domain under which the api should be exposed";
};
webDomain = mkOption {
type = types.str;
example = "firezone.example.com";
description = "The virtual host domain under which the web interface should be exposed";
};
};
openClusterFirewall = mkOption { openClusterFirewall = mkOption {
type = types.bool; type = types.bool;
@ -482,7 +472,7 @@ in
api = componentOptions "api" // { api = componentOptions "api" // {
externalUrl = mkOption { externalUrl = mkOption {
type = types.strMatching "^https://.+/$"; type = types.strMatching "^https://.+/$";
example = "https://api.firezone.example.com/"; example = "https://firezone.example.com/api/";
description = '' description = ''
The external URL under which you will serve the api. You need to The external URL under which you will serve the api. You need to
setup a reverse proxy for TLS termination, either with setup a reverse proxy for TLS termination, either with
@ -690,23 +680,45 @@ in
}) })
# Create a local nginx reverse proxy # Create a local nginx reverse proxy
(mkIf cfg.nginx.enable { (mkIf cfg.nginx.enable {
services.nginx = { services.nginx = mkMerge [
{
enable = true; enable = true;
virtualHosts.${cfg.nginx.webDomain} = { }
(
let
urlComponents = builtins.elemAt (builtins.split "https://([^/]*)(/?.*)" cfg.web.externalUrl) 1;
domain = builtins.elemAt urlComponents 0;
location = builtins.elemAt urlComponents 1;
in
{
virtualHosts.${domain} = {
forceSSL = mkDefault true; forceSSL = mkDefault true;
locations."/" = { locations.${location} = {
proxyPass = "http://${cfg.web.address}:${toString cfg.web.port}"; # The trailing slash is important to strip the location prefix from the request
proxyPass = "http://${cfg.web.address}:${toString cfg.web.port}/";
proxyWebsockets = true; proxyWebsockets = true;
}; };
}; };
virtualHosts.${cfg.nginx.apiDomain} = { }
)
(
let
urlComponents = builtins.elemAt (builtins.split "https://([^/]*)(/?.*)" cfg.api.externalUrl) 1;
domain = builtins.elemAt urlComponents 0;
location = builtins.elemAt urlComponents 1;
in
{
virtualHosts.${domain} = {
forceSSL = mkDefault true; forceSSL = mkDefault true;
locations."/" = { locations.${location} = {
proxyPass = "http://${cfg.api.address}:${toString cfg.api.port}"; # The trailing slash is important to strip the location prefix from the request
proxyPass = "http://${cfg.api.address}:${toString cfg.api.port}/";
proxyWebsockets = true; proxyWebsockets = true;
}; };
}; };
}; }
)
];
}) })
# Specify sensible defaults # Specify sensible defaults
{ {
@ -832,7 +844,7 @@ in
}; };
systemd.services.firezone-initialize = { systemd.services.firezone-initialize = {
description = "Firezone initialization"; description = "Backend initialization service for the Firezone zero-trust access platform";
after = mkIf cfg.enableLocalDB [ "postgresql.service" ]; after = mkIf cfg.enableLocalDB [ "postgresql.service" ];
requires = mkIf cfg.enableLocalDB [ "postgresql.service" ]; requires = mkIf cfg.enableLocalDB [ "postgresql.service" ];
@ -859,7 +871,7 @@ in
}; };
systemd.services.firezone-server-domain = mkIf cfg.domain.enable { systemd.services.firezone-server-domain = mkIf cfg.domain.enable {
description = "Firezone domain server"; description = "Backend domain server for the Firezone zero-trust access platform";
after = [ "firezone-initialize.service" ]; after = [ "firezone-initialize.service" ];
bindsTo = [ "firezone-initialize.service" ]; bindsTo = [ "firezone-initialize.service" ];
wantedBy = [ "firezone.target" ]; wantedBy = [ "firezone.target" ];
@ -870,12 +882,13 @@ in
exec ${getExe cfg.domain.package} start; exec ${getExe cfg.domain.package} start;
''; '';
path = [ pkgs.curl ];
postStart = mkIf cfg.provision.enable '' postStart = mkIf cfg.provision.enable ''
${loadSecretEnvironment "domain"} ${loadSecretEnvironment "domain"}
# Wait for the firezone server to come online # Wait for the firezone server to come online
count=0 count=0
while ! ${getExe cfg.domain.package} pid >/dev/null while [[ "$(curl -s "http://localhost:${toString cfg.domain.settings.HEALTHZ_PORT}" 2>/dev/null || echo)" != '{"status":"ok"}' ]]
do do
sleep 1 sleep 1
if [[ "$count" -eq 30 ]]; then if [[ "$count" -eq 30 ]]; then
@ -885,6 +898,7 @@ in
count=$((count++)) count=$((count++))
done done
sleep 1 # Wait for server to fully come up. Not ideal to use sleep, but at least it works.
ln -sTf ${provisionStateJson} provision-state.json ln -sTf ${provisionStateJson} provision-state.json
${getExe cfg.domain.package} rpc 'Code.eval_file("${./provision.exs}")' ${getExe cfg.domain.package} rpc 'Code.eval_file("${./provision.exs}")'
''; '';
@ -894,7 +908,7 @@ in
}; };
systemd.services.firezone-server-web = mkIf cfg.web.enable { systemd.services.firezone-server-web = mkIf cfg.web.enable {
description = "Firezone web server"; description = "Backend web server for the Firezone zero-trust access platform";
after = [ "firezone-initialize.service" ]; after = [ "firezone-initialize.service" ];
bindsTo = [ "firezone-initialize.service" ]; bindsTo = [ "firezone-initialize.service" ];
wantedBy = [ "firezone.target" ]; wantedBy = [ "firezone.target" ];
@ -910,7 +924,7 @@ in
}; };
systemd.services.firezone-server-api = mkIf cfg.api.enable { systemd.services.firezone-server-api = mkIf cfg.api.enable {
description = "Firezone api server"; description = "Backend api server for the Firezone zero-trust access platform";
after = [ "firezone-initialize.service" ]; after = [ "firezone-initialize.service" ];
bindsTo = [ "firezone-initialize.service" ]; bindsTo = [ "firezone-initialize.service" ];
wantedBy = [ "firezone.target" ]; wantedBy = [ "firezone.target" ];

View file

@ -22,6 +22,10 @@ _inputs: [
firezone-server-domain = prev.callPackage ./firezone-server-domain/package.nix { }; firezone-server-domain = prev.callPackage ./firezone-server-domain/package.nix { };
firezone-server-web = prev.callPackage ./firezone-server-web/package.nix { }; firezone-server-web = prev.callPackage ./firezone-server-web/package.nix { };
firezone-server-api = prev.callPackage ./firezone-server-api/package.nix { }; firezone-server-api = prev.callPackage ./firezone-server-api/package.nix { };
firezone-gateway = prev.callPackage ./firezone-gateway/package.nix { };
firezone-relay = prev.callPackage ./firezone-relay/package.nix { };
firezone-gui-client = prev.callPackage ./firezone-gui-client/package.nix { };
firezone-headless-client = prev.callPackage ./firezone-headless-client/package.nix { };
mdns-repeater = prev.callPackage ./mdns-repeater.nix { }; mdns-repeater = prev.callPackage ./mdns-repeater.nix { };

View file

@ -0,0 +1,39 @@
{
lib,
rustPlatform,
fetchFromGitHub,
nix-update-script,
}:
rustPlatform.buildRustPackage rec {
pname = "firezone-gateway";
version = "1.4.2";
src = fetchFromGitHub {
owner = "oddlama";
repo = "firezone";
rev = "feat/configurable-gateway-interface";
hash = "sha256-dsGQubRs/ZHdAd6MHpa+3K4vGm4OnCdf1yMlGG1AIJk=";
};
useFetchCargoVendor = true;
cargoHash = "sha256-odrexu5mBGmvHNF4cUu9PDIEdxZC451lWV7dsLYT/Sg=";
sourceRoot = "${src.name}/rust";
buildAndTestSubdir = "gateway";
# Required to remove profiling arguments which conflict with this builder
postPatch = ''
rm .cargo/config.toml
'';
passthru.updateScript = nix-update-script { };
meta = {
description = "WireGuard tunnel server for the Firezone zero-trust access platform";
homepage = "https://github.com/firezone/firezone";
license = lib.licenses.asl20;
maintainers = with lib.maintainers; [
oddlama
patrickdag
];
mainProgram = "firezone-gateway";
};
}

View file

@ -0,0 +1,281 @@
diff --git a/rust/Cargo.lock b/rust/Cargo.lock
index 9645ee069..558339b1f 100644
--- a/rust/Cargo.lock
+++ b/rust/Cargo.lock
@@ -951,7 +951,7 @@ dependencies = [
"cocoa-foundation 0.1.2",
"core-foundation 0.9.4",
"core-graphics 0.23.2",
- "foreign-types",
+ "foreign-types 0.5.0",
"libc",
"objc",
]
@@ -967,7 +967,7 @@ dependencies = [
"cocoa-foundation 0.2.0",
"core-foundation 0.10.0",
"core-graphics 0.24.0",
- "foreign-types",
+ "foreign-types 0.5.0",
"libc",
"objc",
]
@@ -1173,7 +1173,7 @@ dependencies = [
"bitflags 1.3.2",
"core-foundation 0.9.4",
"core-graphics-types 0.1.3",
- "foreign-types",
+ "foreign-types 0.5.0",
"libc",
]
@@ -1186,7 +1186,7 @@ dependencies = [
"bitflags 2.6.0",
"core-foundation 0.10.0",
"core-graphics-types 0.2.0",
- "foreign-types",
+ "foreign-types 0.5.0",
"libc",
]
@@ -1999,6 +1999,7 @@ dependencies = [
"firezone-logging",
"firezone-telemetry",
"futures",
+ "hyper-tls",
"native-dialog",
"nix 0.29.0",
"rand 0.8.5",
@@ -2281,6 +2282,15 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
+[[package]]
+name = "foreign-types"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
+dependencies = [
+ "foreign-types-shared 0.1.1",
+]
+
[[package]]
name = "foreign-types"
version = "0.5.0"
@@ -2288,7 +2298,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
dependencies = [
"foreign-types-macros",
- "foreign-types-shared",
+ "foreign-types-shared 0.3.1",
]
[[package]]
@@ -2302,6 +2312,12 @@ dependencies = [
"syn 2.0.87",
]
+[[package]]
+name = "foreign-types-shared"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
+
[[package]]
name = "foreign-types-shared"
version = "0.3.1"
@@ -3035,6 +3051,22 @@ dependencies = [
"tower-service",
]
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
[[package]]
name = "hyper-util"
version = "0.1.9"
@@ -3723,6 +3755,23 @@ dependencies = [
"winapi",
]
+[[package]]
+name = "native-tls"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework",
+ "security-framework-sys",
+ "tempfile",
+]
+
[[package]]
name = "ndk"
version = "0.9.0"
@@ -4286,6 +4335,50 @@ dependencies = [
"pathdiff",
]
+[[package]]
+name = "openssl"
+version = "0.10.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5"
+dependencies = [
+ "bitflags 2.6.0",
+ "cfg-if",
+ "foreign-types 0.3.2",
+ "libc",
+ "once_cell",
+ "openssl-macros",
+ "openssl-sys",
+]
+
+[[package]]
+name = "openssl-macros"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.87",
+]
+
+[[package]]
+name = "openssl-probe"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
+
+[[package]]
+name = "openssl-sys"
+version = "0.9.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
+dependencies = [
+ "cc",
+ "libc",
+ "pkg-config",
+ "vcpkg",
+]
+
[[package]]
name = "opentelemetry"
version = "0.26.0"
@@ -5385,6 +5478,15 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "schannel"
+version = "0.1.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d"
+dependencies = [
+ "windows-sys 0.59.0",
+]
+
[[package]]
name = "schemars"
version = "0.8.21"
@@ -5474,6 +5576,29 @@ dependencies = [
"zbus",
]
+[[package]]
+name = "security-framework"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
+dependencies = [
+ "bitflags 2.6.0",
+ "core-foundation 0.9.4",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
+]
+
+[[package]]
+name = "security-framework-sys"
+version = "2.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
[[package]]
name = "selectors"
version = "0.22.0"
@@ -5947,7 +6072,7 @@ dependencies = [
"bytemuck",
"cfg_aliases",
"core-graphics 0.24.0",
- "foreign-types",
+ "foreign-types 0.5.0",
"js-sys",
"log",
"objc2",
@@ -6786,6 +6911,16 @@ dependencies = [
"syn 2.0.87",
]
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
[[package]]
name = "tokio-rustls"
version = "0.26.0"
@@ -7395,6 +7530,12 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+[[package]]
+name = "vcpkg"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
+
[[package]]
name = "version-compare"
version = "0.2.0"
diff --git a/rust/gui-client/src-tauri/Cargo.toml b/rust/gui-client/src-tauri/Cargo.toml
index 32ca56197..f0475e6bf 100644
--- a/rust/gui-client/src-tauri/Cargo.toml
+++ b/rust/gui-client/src-tauri/Cargo.toml
@@ -25,6 +25,7 @@ firezone-headless-client = { workspace = true }
firezone-logging = { workspace = true }
firezone-telemetry = { workspace = true }
futures = { workspace = true }
+hyper-tls = "0.6.0"
native-dialog = { workspace = true }
rand = { workspace = true }
rustls = { workspace = true }

View file

@ -0,0 +1,153 @@
{
lib,
rustPlatform,
fetchFromGitHub,
nix-update-script,
pkg-config,
dbus,
openssl,
zenity,
cargo-tauri,
gdk-pixbuf,
glib,
gobject-introspection,
gtk3,
kdialog,
libsoup_3,
libayatana-appindicator,
webkitgtk_4_1,
wrapGAppsHook3,
stdenvNoCC,
git,
pnpm_9,
nodejs,
}:
let
version = "1.4.0";
src = fetchFromGitHub {
owner = "firezone";
repo = "firezone";
tag = "gui-client-${version}";
hash = "sha256-gwlkHTFVd1gHwOOIar+5+LEP2ovoyEIMmLfPxYb/OCs=";
# FIXME: remove once https://github.com/firezone/firezone/pull/7808 is released
# FIXME: somehow still required...
postFetch = ''
${lib.getExe git} -C $out apply ${./0000-add-hyper-tls-dependency.patch}
'';
};
frontend = stdenvNoCC.mkDerivation rec {
pname = "firezone-gui-client-frontend";
inherit version src;
pnpmDeps = pnpm_9.fetchDeps {
inherit pname version;
src = "${src}/rust/gui-client";
hash = "sha256-Gc+X4mbpHa8b/Ol6Fee7VFpTPG25LksoAEjFww1WH5I=";
};
pnpmRoot = "rust/gui-client";
nativeBuildInputs = [
pnpm_9.configHook
nodejs
];
buildPhase = ''
runHook preBuild
cd $pnpmRoot
cp node_modules/flowbite/dist/flowbite.min.js src/
pnpm tailwindcss -i src/input.css -o src/output.css
node --max_old_space_size=1024000 ./node_modules/vite/bin/vite.js build
runHook postBuild
'';
installPhase = ''
runHook preInstall
cp -r dist $out
runHook postInstall
'';
};
in
rustPlatform.buildRustPackage rec {
pname = "firezone-gui-client";
inherit version src;
useFetchCargoVendor = true;
cargoHash = "sha256-eqkYwP14kca7Q/P9wc+CvTBmKETTmbd06EBNx3MKLro=";
sourceRoot = "${src.name}/rust";
buildAndTestSubdir = "gui-client";
nativeBuildInputs = [
cargo-tauri.hook
pkg-config
wrapGAppsHook3
];
buildInputs = [
openssl
dbus
gdk-pixbuf
glib
gobject-introspection
gtk3
libsoup_3
libayatana-appindicator
webkitgtk_4_1
];
# Required to remove profiling arguments which conflict with this builder
postPatch = ''
rm .cargo/config.toml
ln -s ${frontend} gui-client/dist
'';
# Required to run tests
preCheck = ''
export XDG_RUNTIME_DIR=$(mktemp -d)
'';
checkFlags = [
# Requires network access
"--skip=notifiers"
];
preFixup = ''
gappsWrapperArgs+=(
# Otherwise blank screen, see https://github.com/tauri-apps/tauri/issues/9304
--set WEBKIT_DISABLE_DMABUF_RENDERER 1
--prefix PATH ":" ${
lib.makeBinPath [
zenity
kdialog
]
}
--prefix LD_LIBRARY_PATH ":" ${
lib.makeLibraryPath [
libayatana-appindicator
]
}
)
'';
passthru = {
inherit frontend;
updateScript = nix-update-script { };
};
meta = {
description = "GUI client for the Firezone zero-trust access platform";
homepage = "https://github.com/firezone/firezone";
license = lib.licenses.asl20;
maintainers = with lib.maintainers; [
oddlama
patrickdag
];
mainProgram = "firezone-gui-client";
};
}

View file

@ -0,0 +1,44 @@
{
lib,
rustPlatform,
fetchFromGitHub,
nix-update-script,
}:
rustPlatform.buildRustPackage rec {
pname = "firezone-headless-client";
version = "1.4.0";
src = fetchFromGitHub {
owner = "oddlama";
repo = "firezone";
rev = "feat/configurable-gateway-interface";
hash = "sha256-dsGQubRs/ZHdAd6MHpa+3K4vGm4OnCdf1yMlGG1AIJk=";
};
useFetchCargoVendor = true;
cargoHash = "sha256-odrexu5mBGmvHNF4cUu9PDIEdxZC451lWV7dsLYT/Sg=";
sourceRoot = "${src.name}/rust";
buildAndTestSubdir = "headless-client";
# Required to remove profiling arguments which conflict with this builder
postPatch = ''
rm .cargo/config.toml
'';
# Required to run tests
preCheck = ''
export XDG_RUNTIME_DIR=$(mktemp -d)
'';
passthru.updateScript = nix-update-script { };
meta = {
description = "CLI client for the Firezone zero-trust access platform";
homepage = "https://github.com/firezone/firezone";
license = lib.licenses.asl20;
maintainers = with lib.maintainers; [
oddlama
patrickdag
];
mainProgram = "firezone-headless-client";
};
}

View file

@ -0,0 +1,39 @@
{
lib,
rustPlatform,
fetchFromGitHub,
nix-update-script,
}:
rustPlatform.buildRustPackage rec {
pname = "firezone-relay";
version = "unstable-2025-01-19";
src = fetchFromGitHub {
owner = "firezone";
repo = "firezone";
rev = "8c9427b7b133e5050be34c2ac0e831c12c08f02c";
hash = "sha256-yccplADHRJQQiKrmHcJ5rvouswHrbx4K6ysnIAoZJR0=";
};
useFetchCargoVendor = true;
cargoHash = "sha256-WpJL5ALFMvyYv3QI5gMazAj6BVr4oyGq+zOo40rxqOE=";
sourceRoot = "${src.name}/rust";
buildAndTestSubdir = "relay";
# Required to remove profiling arguments which conflict with this builder
postPatch = ''
rm .cargo/config.toml
'';
passthru.updateScript = nix-update-script { };
meta = {
description = "STUN/TURN server for the Firezone zero-trust access platform";
homepage = "https://github.com/firezone/firezone";
license = lib.licenses.asl20;
maintainers = with lib.maintainers; [
oddlama
patrickdag
];
mainProgram = "firezone-relay";
};
}

View file

@ -0,0 +1,60 @@
diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex
index 8cd2e8d0f..de55c8e8f 100644
--- a/apps/domain/lib/domain/config/definitions.ex
+++ b/apps/domain/lib/domain/config/definitions.ex
@@ -120,6 +120,7 @@ defmodule Domain.Config.Definitions do
]},
{"Instrumentation",
[
+ :healthz_port,
:instrumentation_client_logs_enabled,
:instrumentation_client_logs_bucket,
:telemetry_metrics_reporter,
@@ -474,6 +475,19 @@ defmodule Domain.Config.Definitions do
## Telemetry
##############################################
+ @doc """
+ The port for the internal healthz endpoint.
+ """
+ defconfig(:healthz_port, :integer,
+ default: 4000,
+ changeset: fn changeset, key ->
+ Ecto.Changeset.validate_number(changeset, key,
+ greater_than: 0,
+ less_than_or_equal_to: 65_535
+ )
+ end
+ )
+
@doc """
Enable or disable the Firezone telemetry collection.
diff --git a/apps/domain/lib/domain/telemetry.ex b/apps/domain/lib/domain/telemetry.ex
index af430358d..d154282b4 100644
--- a/apps/domain/lib/domain/telemetry.ex
+++ b/apps/domain/lib/domain/telemetry.ex
@@ -13,7 +13,7 @@ defmodule Domain.Telemetry do
children = [
# We start a /healthz endpoint that is used for liveness probes
- {Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: 4000},
+ {Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: Keyword.get(config, :healthz_port)},
# Telemetry poller will execute the given period measurements
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
diff --git a/config/runtime.exs b/config/runtime.exs
index 7817942fa..c6dfe9c31 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -211,7 +211,9 @@ if config_env() == :prod do
otlp_endpoint: System.get_env("OTLP_ENDPOINT")
end
- config :domain, Domain.Telemetry, metrics_reporter: compile_config!(:telemetry_metrics_reporter)
+ config :domain, Domain.Telemetry,
+ healthz_port: compile_config!(:healthz_port),
+ metrics_reporter: compile_config!(:telemetry_metrics_reporter)
if telemetry_metrics_reporter = compile_config!(:telemetry_metrics_reporter) do
config :domain, telemetry_metrics_reporter, compile_config!(:telemetry_metrics_reporter_opts)

View file

@ -0,0 +1,100 @@
diff --git a/apps/domain/lib/domain/config.ex b/apps/domain/lib/domain/config.ex
index 11fdcc3a1..9254f210d 100644
--- a/apps/domain/lib/domain/config.ex
+++ b/apps/domain/lib/domain/config.ex
@@ -42,6 +42,24 @@ defmodule Domain.Config do
end
end
+ @doc """
+ Similar to `compile_config/2` but returns nil if the configuration is invalid.
+
+ This function does not resolve values from the database because it's intended use is during
+ compilation and before application boot (in `config/runtime.exs`).
+
+ If you need to resolve values from the database, use `fetch_config/1` or `fetch_config!/1`.
+ """
+ def compile_config(module \\ Definitions, key, env_config \\ System.get_env()) do
+ case Fetcher.fetch_source_and_config(module, key, %{}, env_config) do
+ {:ok, _source, value} ->
+ value
+
+ {:error, reason} ->
+ nil
+ end
+ end
+
def config_changeset(changeset, schema_key, config_key \\ nil) do
config_key = config_key || schema_key
diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex
index 8cd2e8d0f..f27d67c69 100644
--- a/apps/domain/lib/domain/config/definitions.ex
+++ b/apps/domain/lib/domain/config/definitions.ex
@@ -61,6 +61,7 @@ defmodule Domain.Config.Definitions do
{"Database",
[
:database_host,
+ :database_socket_dir,
:database_port,
:database_name,
:database_user,
@@ -255,6 +256,11 @@ defmodule Domain.Config.Definitions do
"""
defconfig(:database_host, :string, default: "postgres")
+ @doc """
+ PostgreSQL socket directory (takes precedence over hostname).
+ """
+ defconfig(:database_socket_dir, :string, default: nil)
+
@doc """
PostgreSQL port.
"""
diff --git a/config/runtime.exs b/config/runtime.exs
index 7817942fa..14cbe182f 100644
--- a/config/runtime.exs
+++ b/config/runtime.exs
@@ -1,22 +1,31 @@
import Config
if config_env() == :prod do
- import Domain.Config, only: [compile_config!: 1]
+ import Domain.Config, only: [compile_config!: 1, compile_config: 1]
###############################
##### Domain ##################
###############################
- config :domain, Domain.Repo,
- database: compile_config!(:database_name),
- username: compile_config!(:database_user),
- hostname: compile_config!(:database_host),
- port: compile_config!(:database_port),
- password: compile_config!(:database_password),
- pool_size: compile_config!(:database_pool_size),
- ssl: compile_config!(:database_ssl_enabled),
- ssl_opts: compile_config!(:database_ssl_opts),
- parameters: compile_config!(:database_parameters)
+ config :domain,
+ Domain.Repo,
+ [
+ {:database, compile_config!(:database_name)},
+ {:username, compile_config!(:database_user)},
+ {:port, compile_config!(:database_port)},
+ {:pool_size, compile_config!(:database_pool_size)},
+ {:ssl, compile_config!(:database_ssl_enabled)},
+ {:ssl_opts, compile_config!(:database_ssl_opts)},
+ {:parameters, compile_config!(:database_parameters)}
+ ] ++
+ if(compile_config(:database_password),
+ do: [{:password, compile_config!(:database_password)}],
+ else: []
+ ) ++
+ if(compile_config(:database_socket_dir),
+ do: [{:socket_dir, compile_config!(:database_socket_dir)}],
+ else: [{:hostname, compile_config!(:database_host)}]
+ )
config :domain, Domain.Tokens,
key_base: compile_config!(:tokens_key_base),

View file

@ -1,28 +1,8 @@
diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex diff --git a/apps/domain/lib/domain/config/definitions.ex b/apps/domain/lib/domain/config/definitions.ex
index 8cd2e8d0f..92e18b10b 100644 index 8cd2e8d0f..2e6ca054b 100644
--- a/apps/domain/lib/domain/config/definitions.ex --- a/apps/domain/lib/domain/config/definitions.ex
+++ b/apps/domain/lib/domain/config/definitions.ex +++ b/apps/domain/lib/domain/config/definitions.ex
@@ -61,6 +61,7 @@ defmodule Domain.Config.Definitions do @@ -584,6 +584,7 @@ defmodule Domain.Config.Definitions do
{"Database",
[
:database_host,
+ :database_socket_dir,
:database_port,
:database_name,
:database_user,
@@ -255,6 +256,11 @@ defmodule Domain.Config.Definitions do
"""
defconfig(:database_host, :string, default: "postgres")
+ @doc """
+ PostgreSQL socket directory (takes precedence over hostname).
+ """
+ defconfig(:database_socket_dir, :string, default: nil)
+
@doc """
PostgreSQL port.
"""
@@ -584,6 +590,7 @@ defmodule Domain.Config.Definitions do
Swoosh.Adapters.Mailgun, Swoosh.Adapters.Mailgun,
Swoosh.Adapters.Mailjet, Swoosh.Adapters.Mailjet,
Swoosh.Adapters.Mandrill, Swoosh.Adapters.Mandrill,
@ -30,21 +10,8 @@ index 8cd2e8d0f..92e18b10b 100644
Swoosh.Adapters.Postmark, Swoosh.Adapters.Postmark,
Swoosh.Adapters.ProtonBridge, Swoosh.Adapters.ProtonBridge,
Swoosh.Adapters.SMTP, Swoosh.Adapters.SMTP,
diff --git a/apps/domain/lib/domain/telemetry.ex b/apps/domain/lib/domain/telemetry.ex
index af430358d..a544e706e 100644
--- a/apps/domain/lib/domain/telemetry.ex
+++ b/apps/domain/lib/domain/telemetry.ex
@@ -13,7 +13,7 @@ defmodule Domain.Telemetry do
children = [
# We start a /healthz endpoint that is used for liveness probes
- {Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: 4000},
+ {Bandit, plug: Telemetry.HealthzPlug, scheme: :http, port: System.get_env("HEALTHZ_PORT") |> String.to_integer()},
# Telemetry poller will execute the given period measurements
# every 10_000ms. Learn more here: https://hexdocs.pm/telemetry_metrics
diff --git a/apps/web/lib/web/live/sign_up.ex b/apps/web/lib/web/live/sign_up.ex diff --git a/apps/web/lib/web/live/sign_up.ex b/apps/web/lib/web/live/sign_up.ex
index c4e06bc58..89533fb81 100644 index dfbc94d9b..638e3b373 100644
--- a/apps/web/lib/web/live/sign_up.ex --- a/apps/web/lib/web/live/sign_up.ex
+++ b/apps/web/lib/web/live/sign_up.ex +++ b/apps/web/lib/web/live/sign_up.ex
@@ -1,5 +1,6 @@ @@ -1,5 +1,6 @@
@ -54,55 +21,29 @@ index c4e06bc58..89533fb81 100644
alias Domain.{Auth, Accounts, Actors, Config} alias Domain.{Auth, Accounts, Actors, Config}
alias Web.Registration alias Web.Registration
@@ -358,7 +359,8 @@ defmodule Web.SignUp do @@ -358,7 +359,9 @@ defmodule Web.SignUp do
{:noreply, assign(socket, form: to_form(changeset))} {:noreply, assign(socket, form: to_form(changeset))}
- {:error, :send_email, _reason, _effects_so_far} -> - {:error, :send_email, _reason, _effects_so_far} ->
+ {:error, :send_email, reason, _effects_so_far} -> + {:error, :send_email, reason, _effects_so_far} ->
+ Logger.info("failed to send email", reason: inspect(reason)) + Logger.warning("Failed to send sign_up email", reason: inspect(reason))
+
changeset = changeset =
Ecto.Changeset.add_error( Ecto.Changeset.add_error(
changeset, changeset,
diff --git a/config/runtime.exs b/config/runtime.exs diff --git a/config/runtime.exs b/config/runtime.exs
index 15037e0a3..475c4ddfb 100644 index 7817942fa..8704ccde5 100644
--- a/config/runtime.exs --- a/config/runtime.exs
+++ b/config/runtime.exs +++ b/config/runtime.exs
@@ -8,15 +8,17 @@ if config_env() == :prod do @@ -228,7 +228,21 @@ if config_env() == :prod do
###############################
config :domain, Domain.Repo,
- database: compile_config!(:database_name),
- username: compile_config!(:database_user),
- hostname: compile_config!(:database_host),
- port: compile_config!(:database_port),
- password: compile_config!(:database_password),
- pool_size: compile_config!(:database_pool_size),
- ssl: compile_config!(:database_ssl_enabled),
- ssl_opts: compile_config!(:database_ssl_opts),
- parameters: compile_config!(:database_parameters)
+ [
+ {:database, compile_config!(:database_name)},
+ {:username, compile_config!(:database_user)},
+ {:port, compile_config!(:database_port)},
+ {:pool_size, compile_config!(:database_pool_size)},
+ {:ssl, compile_config!(:database_ssl_enabled)},
+ {:ssl_opts, compile_config!(:database_ssl_opts)},
+ {:parameters, compile_config!(:database_parameters)}
+ ]
+ ++ (if System.get_env("DATABASE_PASSWORD"), do: [{:password, compile_config!(:database_password)}], else: [])
+ ++ (if System.get_env("DATABASE_SOCKET_DIR"), do: [{:socket_dir, compile_config!(:database_socket_dir)}], else: [{:hostname, compile_config!(:database_host)}])
config :domain, Domain.Tokens,
key_base: compile_config!(:tokens_key_base),
@@ -226,8 +228,15 @@ if config_env() == :prod do
config :domain,
Domain.Mailer,
[ [
- adapter: compile_config!(:outbound_email_adapter), adapter: compile_config!(:outbound_email_adapter),
- from_email: compile_config!(:outbound_email_from) from_email: compile_config!(:outbound_email_from)
+ adapter: compile_config!(:outbound_email_adapter), - ] ++ compile_config!(:outbound_email_adapter_opts)
+ from_email: compile_config!(:outbound_email_from), + ] ++
+ (if System.get_env("OUTBOUND_EMAIL_ADAPTER") == "Elixir.Swoosh.Adapters.Mua" do
+ [
+ protocol: String.to_atom(System.get_env("OUTBOUND_EMAIL_SMTP_PROTOCOL")), + protocol: String.to_atom(System.get_env("OUTBOUND_EMAIL_SMTP_PROTOCOL")),
+ relay: System.get_env("OUTBOUND_EMAIL_SMTP_HOST"), + relay: System.get_env("OUTBOUND_EMAIL_SMTP_HOST"),
+ port: String.to_integer(System.get_env("OUTBOUND_EMAIL_SMTP_PORT")), + port: String.to_integer(System.get_env("OUTBOUND_EMAIL_SMTP_PORT")),
@ -110,9 +51,14 @@ index 15037e0a3..475c4ddfb 100644
+ username: System.get_env("OUTBOUND_EMAIL_SMTP_USERNAME"), + username: System.get_env("OUTBOUND_EMAIL_SMTP_USERNAME"),
+ password: System.get_env("OUTBOUND_EMAIL_SMTP_PASSWORD") + password: System.get_env("OUTBOUND_EMAIL_SMTP_PASSWORD")
+ ] + ]
] ++ compile_config!(:outbound_email_adapter_opts) + ]
+ else
+ []
+ end) ++
+ compile_config!(:outbound_email_adapter_opts)
config :workos, WorkOS.Client, config :workos, WorkOS.Client,
api_key: compile_config!(:workos_api_key),
diff --git a/mix.exs b/mix.exs diff --git a/mix.exs b/mix.exs
index 12782d631..dee1245d2 100644 index 12782d631..dee1245d2 100644
--- a/mix.exs --- a/mix.exs
@ -129,7 +75,7 @@ index 12782d631..dee1245d2 100644
end end
diff --git a/mix.lock b/mix.lock diff --git a/mix.lock b/mix.lock
index 8c4b65959..3d2f9faca 100644 index 757829003..47775677d 100644
--- a/mix.lock --- a/mix.lock
+++ b/mix.lock +++ b/mix.lock
@@ -50,11 +50,13 @@ @@ -50,11 +50,13 @@

View file

@ -24,7 +24,11 @@ beamPackages.mixRelease rec {
hash = "sha256-yccplADHRJQQiKrmHcJ5rvouswHrbx4K6ysnIAoZJR0="; hash = "sha256-yccplADHRJQQiKrmHcJ5rvouswHrbx4K6ysnIAoZJR0=";
} }
}/elixir"; }/elixir";
patches = [ ./a.patch ]; patches = [
./0000-add-healthz-port.patch
./0001-postgres-socket.patch
./0002-add-mua.patch
];
pnpmDeps = pnpm_9.fetchDeps { pnpmDeps = pnpm_9.fetchDeps {
inherit pname version; inherit pname version;