1{ 2 lib, 3 pkgs, 4 config, 5 ... 6}: 7let 8 inherit (lib) 9 boolToString 10 getExe 11 mkEnableOption 12 mkIf 13 mkOption 14 mkPackageOption 15 types 16 ; 17 18 cfg = config.services.firezone.relay; 19in 20{ 21 options = { 22 services.firezone.relay = { 23 enable = mkEnableOption "the firezone relay server"; 24 package = mkPackageOption pkgs "firezone-relay" { }; 25 26 name = mkOption { 27 type = types.str; 28 example = "My relay"; 29 description = "The name of this gateway as shown in firezone"; 30 }; 31 32 publicIpv4 = mkOption { 33 type = types.nullOr types.str; 34 default = null; 35 description = "The public ipv4 address of this relay"; 36 }; 37 38 publicIpv6 = mkOption { 39 type = types.nullOr types.str; 40 default = null; 41 description = "The public ipv6 address of this relay"; 42 }; 43 44 openFirewall = mkOption { 45 type = types.bool; 46 default = true; 47 description = "Opens up the main STUN port and the TURN allocation range."; 48 }; 49 50 port = mkOption { 51 type = types.port; 52 default = 3478; 53 description = "The port to listen on for STUN messages"; 54 }; 55 56 lowestPort = mkOption { 57 type = types.port; 58 default = 49152; 59 description = "The lowest port to use in TURN allocation"; 60 }; 61 62 highestPort = mkOption { 63 type = types.port; 64 default = 65535; 65 description = "The highest port to use in TURN allocation"; 66 }; 67 68 apiUrl = mkOption { 69 type = types.strMatching "^wss://.+/$"; 70 example = "wss://firezone.example.com/api/"; 71 description = '' 72 The URL of your firezone server's API. This should be the same 73 as your server's setting for {option}`services.firezone.server.settings.api.externalUrl`, 74 but with `wss://` instead of `https://`. 75 ''; 76 }; 77 78 tokenFile = mkOption { 79 type = types.path; 80 example = "/run/secrets/firezone-relay-token"; 81 description = '' 82 A file containing the firezone relay token. Do not use a nix-store path here 83 as it will make the token publicly readable! 84 85 This file will be passed via systemd credentials, it should only be accessible 86 by the root user. 87 ''; 88 }; 89 90 logLevel = mkOption { 91 type = types.str; 92 default = "info"; 93 description = '' 94 The log level for the firezone application. See 95 [RUST_LOG](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) 96 for the format. 97 ''; 98 }; 99 100 enableTelemetry = mkEnableOption "telemetry"; 101 }; 102 }; 103 104 config = mkIf cfg.enable { 105 assertions = [ 106 { 107 assertion = cfg.publicIpv4 != null || cfg.publicIpv6 != null; 108 message = "At least one of `services.firezone.relay.publicIpv4` and `services.firezone.relay.publicIpv6` must be set"; 109 } 110 ]; 111 112 networking.firewall.allowedUDPPorts = mkIf cfg.openFirewall [ cfg.port ]; 113 networking.firewall.allowedUDPPortRanges = mkIf cfg.openFirewall [ 114 { 115 from = cfg.lowestPort; 116 to = cfg.highestPort; 117 } 118 ]; 119 120 systemd.services.firezone-relay = { 121 description = "relay service for the Firezone zero-trust access platform"; 122 after = [ "network.target" ]; 123 wantedBy = [ "multi-user.target" ]; 124 125 path = [ pkgs.util-linux ]; 126 script = '' 127 # If FIREZONE_ID is not given by the user, use a persisted (or newly generated) uuid. 128 if [[ -z "''${FIREZONE_ID:-}" ]]; then 129 if [[ ! -e relay_id ]]; then 130 uuidgen -r > relay_id 131 fi 132 export FIREZONE_ID=$(< relay_id) 133 fi 134 135 export FIREZONE_TOKEN=$(< "$CREDENTIALS_DIRECTORY/firezone-token") 136 exec ${getExe cfg.package} 137 ''; 138 139 environment = { 140 FIREZONE_API_URL = cfg.apiUrl; 141 FIREZONE_NAME = cfg.name; 142 FIREZONE_TELEMETRY = boolToString cfg.enableTelemetry; 143 144 PUBLIC_IP4_ADDR = cfg.publicIpv4; 145 PUBLIC_IP6_ADDR = cfg.publicIpv6; 146 147 LISTEN_PORT = toString cfg.port; 148 LOWEST_PORT = toString cfg.lowestPort; 149 HIGHEST_PORT = toString cfg.highestPort; 150 151 RUST_LOG = cfg.logLevel; 152 LOG_FORMAT = "human"; 153 }; 154 155 serviceConfig = { 156 Type = "exec"; 157 DynamicUser = true; 158 User = "firezone-relay"; 159 LoadCredential = [ "firezone-token:${cfg.tokenFile}" ]; 160 161 StateDirectory = "firezone-relay"; 162 WorkingDirectory = "/var/lib/firezone-relay"; 163 164 Restart = "on-failure"; 165 RestartSec = 10; 166 167 LockPersonality = true; 168 MemoryDenyWriteExecute = true; 169 NoNewPrivileges = true; 170 PrivateMounts = true; 171 PrivateTmp = true; 172 PrivateUsers = false; 173 ProcSubset = "pid"; 174 ProtectClock = true; 175 ProtectControlGroups = true; 176 ProtectHome = true; 177 ProtectHostname = true; 178 ProtectKernelLogs = true; 179 ProtectKernelModules = true; 180 ProtectKernelTunables = true; 181 ProtectProc = "invisible"; 182 ProtectSystem = "strict"; 183 RestrictAddressFamilies = [ 184 "AF_INET" 185 "AF_INET6" 186 "AF_NETLINK" 187 ]; 188 RestrictNamespaces = true; 189 RestrictRealtime = true; 190 RestrictSUIDSGID = true; 191 SystemCallArchitectures = "native"; 192 SystemCallFilter = "@system-service"; 193 UMask = "077"; 194 }; 195 }; 196 }; 197 198 meta.maintainers = with lib.maintainers; [ 199 oddlama 200 patrickdag 201 ]; 202}