lol

nixos/fedimintd: init services

+345
+2
nixos/doc/manual/release-notes/rl-2411.section.md
··· 163 163 164 164 - [Veilid](https://veilid.com), a headless server that enables privacy-focused data sharing and messaging on a peer-to-peer network. Available as [services.veilid](#opt-services.veilid.enable). 165 165 166 + - [Fedimint](https://github.com/fedimint/fedimint), a module based system for building federated applications (Federated E-Cash Mint). Available as [services.fedimintd](#opt-services.fedimintd). 167 + 166 168 ## Backward Incompatibilities {#sec-release-24.11-incompatibilities} 167 169 168 170 - The `sound` options have been removed or renamed, as they had a lot of unintended side effects. See [below](#sec-release-24.11-migration-sound) for details.
+1
nixos/modules/module-list.nix
··· 1031 1031 ./services/networking/expressvpn.nix 1032 1032 ./services/networking/fakeroute.nix 1033 1033 ./services/networking/fastnetmon-advanced.nix 1034 + ./services/networking/fedimintd.nix 1034 1035 ./services/networking/ferm.nix 1035 1036 ./services/networking/firefox-syncserver.nix 1036 1037 ./services/networking/fireqos.nix
+304
nixos/modules/services/networking/fedimintd.nix
··· 1 + { 2 + config, 3 + lib, 4 + pkgs, 5 + ... 6 + }: 7 + let 8 + inherit (lib) 9 + concatLists 10 + filterAttrs 11 + mapAttrs' 12 + mapAttrsToList 13 + mkEnableOption 14 + mkIf 15 + mkOption 16 + mkOverride 17 + mkPackageOption 18 + nameValuePair 19 + recursiveUpdate 20 + types 21 + ; 22 + 23 + fedimintdOpts = 24 + { 25 + config, 26 + lib, 27 + name, 28 + ... 29 + }: 30 + { 31 + options = { 32 + enable = mkEnableOption "fedimintd"; 33 + 34 + package = mkPackageOption pkgs "fedimint" { }; 35 + 36 + environment = mkOption { 37 + type = types.attrsOf types.str; 38 + description = "Extra Environment variables to pass to the fedimintd."; 39 + default = { 40 + RUST_BACKTRACE = "1"; 41 + }; 42 + example = { 43 + RUST_LOG = "info,fm=debug"; 44 + RUST_BACKTRACE = "1"; 45 + }; 46 + }; 47 + 48 + p2p = { 49 + openFirewall = mkOption { 50 + type = types.bool; 51 + default = true; 52 + description = "Opens port in firewall for fedimintd's p2p port"; 53 + }; 54 + port = mkOption { 55 + type = types.port; 56 + default = 8173; 57 + description = "Port to bind on for p2p connections from peers"; 58 + }; 59 + bind = mkOption { 60 + type = types.str; 61 + default = "0.0.0.0"; 62 + description = "Address to bind on for p2p connections from peers"; 63 + }; 64 + url = mkOption { 65 + type = types.str; 66 + example = "fedimint://p2p.myfedimint.com"; 67 + description = '' 68 + Public address for p2p connections from peers 69 + ''; 70 + }; 71 + }; 72 + api = { 73 + openFirewall = mkOption { 74 + type = types.bool; 75 + default = false; 76 + description = "Opens port in firewall for fedimintd's api port"; 77 + }; 78 + port = mkOption { 79 + type = types.port; 80 + default = 8174; 81 + description = "Port to bind on for API connections relied by the reverse proxy/tls terminator."; 82 + }; 83 + bind = mkOption { 84 + type = types.str; 85 + default = "127.0.0.1"; 86 + description = "Address to bind on for API connections relied by the reverse proxy/tls terminator."; 87 + }; 88 + url = mkOption { 89 + type = types.str; 90 + description = '' 91 + Public URL of the API address of the reverse proxy/tls terminator. Usually starting with `wss://`. 92 + ''; 93 + }; 94 + }; 95 + bitcoin = { 96 + network = mkOption { 97 + type = types.str; 98 + default = "signet"; 99 + example = "bitcoin"; 100 + description = "Bitcoin network to participate in."; 101 + }; 102 + rpc = { 103 + url = mkOption { 104 + type = types.str; 105 + default = "http://127.0.0.1:38332"; 106 + example = "signet"; 107 + description = "Bitcoin node (bitcoind/electrum/esplora) address to connect to"; 108 + }; 109 + 110 + kind = mkOption { 111 + type = types.str; 112 + default = "bitcoind"; 113 + example = "electrum"; 114 + description = "Kind of a bitcoin node."; 115 + }; 116 + 117 + secretFile = mkOption { 118 + type = types.nullOr types.path; 119 + default = null; 120 + description = '' 121 + If set the URL specified in `bitcoin.rpc.url` will get the content of this file added 122 + as an URL password, so `http://user@example.com` will turn into `http://user:SOMESECRET@example.com`. 123 + 124 + Example: 125 + 126 + `/etc/nix-bitcoin-secrets/bitcoin-rpcpassword-public` (for nix-bitcoin default) 127 + ''; 128 + }; 129 + }; 130 + }; 131 + 132 + consensus.finalityDelay = mkOption { 133 + type = types.ints.unsigned; 134 + default = 10; 135 + description = "Consensus peg-in finality delay."; 136 + }; 137 + 138 + dataDir = mkOption { 139 + type = types.path; 140 + default = "/var/lib/fedimintd-${name}/"; 141 + readOnly = true; 142 + description = '' 143 + Path to the data dir fedimintd will use to store its data. 144 + Note that due to using the DynamicUser feature of systemd, this value should not be changed 145 + and is set to be read only. 146 + ''; 147 + }; 148 + 149 + nginx = { 150 + enable = mkOption { 151 + type = types.bool; 152 + default = false; 153 + description = '' 154 + Whether to configure nginx for fedimintd 155 + ''; 156 + }; 157 + fqdn = mkOption { 158 + type = types.str; 159 + example = "api.myfedimint.com"; 160 + description = "Public domain of the API address of the reverse proxy/tls terminator."; 161 + }; 162 + config = mkOption { 163 + type = types.submodule ( 164 + recursiveUpdate (import ../web-servers/nginx/vhost-options.nix { 165 + inherit config lib; 166 + }) { } 167 + ); 168 + default = { }; 169 + description = "Overrides to the nginx vhost section for api"; 170 + }; 171 + }; 172 + }; 173 + }; 174 + in 175 + { 176 + options = { 177 + services.fedimintd = mkOption { 178 + type = types.attrsOf (types.submodule fedimintdOpts); 179 + default = { }; 180 + description = "Specification of one or more fedimintd instances."; 181 + }; 182 + }; 183 + 184 + config = 185 + let 186 + eachFedimintd = filterAttrs (fedimintdName: cfg: cfg.enable) config.services.fedimintd; 187 + eachFedimintdNginx = filterAttrs (fedimintdName: cfg: cfg.nginx.enable) eachFedimintd; 188 + in 189 + mkIf (eachFedimintd != { }) { 190 + 191 + networking.firewall.allowedTCPPorts = concatLists ( 192 + mapAttrsToList ( 193 + fedimintdName: cfg: 194 + (lib.optional cfg.api.openFirewall cfg.api.port ++ lib.optional cfg.p2p.openFirewall cfg.p2p.port) 195 + ) eachFedimintd 196 + ); 197 + 198 + systemd.services = mapAttrs' ( 199 + fedimintdName: cfg: 200 + (nameValuePair "fedimintd-${fedimintdName}" ( 201 + let 202 + startScript = pkgs.writeShellScript "fedimintd-start" ( 203 + ( 204 + if cfg.bitcoin.rpc.secretFile != null then 205 + '' 206 + secret=$(${pkgs.coreutils}/bin/head -n 1 "${cfg.bitcoin.rpc.secretFile}") 207 + prefix="''${FM_BITCOIN_RPC_URL%*@*}" # Everything before the last '@' 208 + suffix="''${FM_BITCOIN_RPC_URL##*@}" # Everything after the last '@' 209 + FM_BITCOIN_RPC_URL="''${prefix}:''${secret}@''${suffix}" 210 + '' 211 + else 212 + "" 213 + ) 214 + + '' 215 + exec ${cfg.package}/bin/fedimintd 216 + '' 217 + ); 218 + in 219 + { 220 + description = "Fedimint Server"; 221 + documentation = [ "https://github.com/fedimint/fedimint/" ]; 222 + wantedBy = [ "multi-user.target" ]; 223 + environment = lib.mkMerge [ 224 + { 225 + FM_BIND_P2P = "${cfg.p2p.bind}:${toString cfg.p2p.port}"; 226 + FM_BIND_API = "${cfg.api.bind}:${toString cfg.api.port}"; 227 + FM_P2P_URL = cfg.p2p.url; 228 + FM_API_URL = cfg.api.url; 229 + FM_DATA_DIR = cfg.dataDir; 230 + FM_BITCOIN_NETWORK = cfg.bitcoin.network; 231 + FM_BITCOIN_RPC_URL = cfg.bitcoin.rpc.url; 232 + FM_BITCOIN_RPC_KIND = cfg.bitcoin.rpc.kind; 233 + } 234 + cfg.environment 235 + ]; 236 + serviceConfig = { 237 + DynamicUser = true; 238 + 239 + StateDirectory = "fedimintd-${fedimintdName}"; 240 + StateDirectoryMode = "0700"; 241 + ExecStart = startScript; 242 + 243 + Restart = "always"; 244 + RestartSec = 10; 245 + StartLimitBurst = 5; 246 + UMask = "007"; 247 + LimitNOFILE = "100000"; 248 + 249 + LockPersonality = true; 250 + MemoryDenyWriteExecute = true; 251 + NoNewPrivileges = true; 252 + PrivateDevices = true; 253 + PrivateMounts = true; 254 + PrivateTmp = true; 255 + ProtectClock = true; 256 + ProtectControlGroups = true; 257 + ProtectHostname = true; 258 + ProtectKernelLogs = true; 259 + ProtectKernelModules = true; 260 + ProtectKernelTunables = true; 261 + ProtectSystem = "full"; 262 + RestrictAddressFamilies = [ 263 + "AF_INET" 264 + "AF_INET6" 265 + ]; 266 + RestrictNamespaces = true; 267 + RestrictRealtime = true; 268 + SystemCallArchitectures = "native"; 269 + SystemCallFilter = [ 270 + "@system-service" 271 + "~@privileged" 272 + ]; 273 + }; 274 + } 275 + )) 276 + ) eachFedimintd; 277 + 278 + services.nginx.virtualHosts = mapAttrs' ( 279 + fedimintdName: cfg: 280 + (nameValuePair cfg.nginx.fqdn ( 281 + lib.mkMerge [ 282 + cfg.nginx.config 283 + 284 + { 285 + # Note: we want by default to enable OpenSSL, but it seems anything 100 and above is 286 + # overriden by default value from vhost-options.nix 287 + enableACME = mkOverride 99 true; 288 + forceSSL = mkOverride 99 true; 289 + # Currently Fedimint API only support JsonRPC on `/ws/` endpoint, so no need to handle `/` 290 + locations."/ws/" = { 291 + proxyPass = "http://127.0.0.1:${toString cfg.api.port}/"; 292 + proxyWebsockets = true; 293 + extraConfig = '' 294 + proxy_pass_header Authorization; 295 + ''; 296 + }; 297 + } 298 + ] 299 + )) 300 + ) eachFedimintdNginx; 301 + }; 302 + 303 + meta.maintainers = with lib.maintainers; [ dpc ]; 304 + }
+1
nixos/tests/all-tests.nix
··· 322 322 fancontrol = handleTest ./fancontrol.nix {}; 323 323 fanout = handleTest ./fanout.nix {}; 324 324 fcitx5 = handleTest ./fcitx5 {}; 325 + fedimintd = runTest ./fedimintd.nix; 325 326 fenics = handleTest ./fenics.nix {}; 326 327 ferm = handleTest ./ferm.nix {}; 327 328 ferretdb = handleTest ./ferretdb.nix {};
+37
nixos/tests/fedimintd.nix
··· 1 + # This test runs the fedimintd and verifies that it starts 2 + 3 + { pkgs, ... }: 4 + 5 + { 6 + name = "fedimintd"; 7 + 8 + meta = with pkgs.lib.maintainers; { 9 + maintainers = [ dpc ]; 10 + }; 11 + 12 + nodes.machine = 13 + { ... }: 14 + { 15 + services.fedimintd."mainnet" = { 16 + enable = true; 17 + p2p = { 18 + url = "fedimint://example.com"; 19 + }; 20 + api = { 21 + url = "wss://example.com"; 22 + }; 23 + environment = { 24 + "FM_REL_NOTES_ACK" = "0_4_xyz"; 25 + }; 26 + }; 27 + }; 28 + 29 + testScript = 30 + { nodes, ... }: 31 + '' 32 + start_all() 33 + 34 + machine.wait_for_unit("fedimintd-mainnet.service") 35 + machine.wait_for_open_port(${toString nodes.machine.services.fedimintd.mainnet.api.port}) 36 + ''; 37 + }