vuls: init at 0.27.0
[NixPkgs.git] / nixos / modules / services / networking / fedimintd.nix
blobc7d93854e21ae6cdb1693eaaa12ed3e5349dea09
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     ;
23   fedimintdOpts =
24     {
25       config,
26       lib,
27       name,
28       ...
29     }:
30     {
31       options = {
32         enable = mkEnableOption "fedimintd";
34         package = mkPackageOption pkgs "fedimint" { };
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         };
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:8173";
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             };
110             kind = mkOption {
111               type = types.str;
112               default = "bitcoind";
113               example = "electrum";
114               description = "Kind of a bitcoin node.";
115             };
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`.
124                 Example:
126                 `/etc/nix-bitcoin-secrets/bitcoin-rpcpassword-public` (for nix-bitcoin default)
127               '';
128             };
129           };
130         };
132         consensus.finalityDelay = mkOption {
133           type = types.ints.unsigned;
134           default = 10;
135           description = "Consensus peg-in finality delay.";
136         };
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         };
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           path = mkOption {
163             type = types.str;
164             example = "/";
165             default = "/ws/";
166             description = "Path to host the API on and forward to the daemon's api port";
167           };
168           config = mkOption {
169             type = types.submodule (
170               recursiveUpdate (import ../web-servers/nginx/vhost-options.nix {
171                 inherit config lib;
172               }) { }
173             );
174             default = { };
175             description = "Overrides to the nginx vhost section for api";
176           };
177         };
178       };
179     };
182   options = {
183     services.fedimintd = mkOption {
184       type = types.attrsOf (types.submodule fedimintdOpts);
185       default = { };
186       description = "Specification of one or more fedimintd instances.";
187     };
188   };
190   config =
191     let
192       eachFedimintd = filterAttrs (fedimintdName: cfg: cfg.enable) config.services.fedimintd;
193       eachFedimintdNginx = filterAttrs (fedimintdName: cfg: cfg.nginx.enable) eachFedimintd;
194     in
195     mkIf (eachFedimintd != { }) {
197       networking.firewall.allowedTCPPorts = concatLists (
198         mapAttrsToList (
199           fedimintdName: cfg:
200           (lib.optional cfg.api.openFirewall cfg.api.port ++ lib.optional cfg.p2p.openFirewall cfg.p2p.port)
201         ) eachFedimintd
202       );
204       systemd.services = mapAttrs' (
205         fedimintdName: cfg:
206         (nameValuePair "fedimintd-${fedimintdName}" (
207           let
208             startScript = pkgs.writeShellScript "fedimintd-start" (
209               (
210                 if cfg.bitcoin.rpc.secretFile != null then
211                   ''
212                     secret=$(${pkgs.coreutils}/bin/head -n 1 "${cfg.bitcoin.rpc.secretFile}")
213                     prefix="''${FM_BITCOIN_RPC_URL%*@*}"  # Everything before the last '@'
214                     suffix="''${FM_BITCOIN_RPC_URL##*@}"  # Everything after the last '@'
215                     FM_BITCOIN_RPC_URL="''${prefix}:''${secret}@''${suffix}"
216                   ''
217                 else
218                   ""
219               )
220               + ''
221                 exec ${cfg.package}/bin/fedimintd
222               ''
223             );
224           in
225           {
226             description = "Fedimint Server";
227             documentation = [ "https://github.com/fedimint/fedimint/" ];
228             wantedBy = [ "multi-user.target" ];
229             environment = lib.mkMerge [
230               {
231                 FM_BIND_P2P = "${cfg.p2p.bind}:${toString cfg.p2p.port}";
232                 FM_BIND_API = "${cfg.api.bind}:${toString cfg.api.port}";
233                 FM_P2P_URL = cfg.p2p.url;
234                 FM_API_URL = cfg.api.url;
235                 FM_DATA_DIR = cfg.dataDir;
236                 FM_BITCOIN_NETWORK = cfg.bitcoin.network;
237                 FM_BITCOIN_RPC_URL = cfg.bitcoin.rpc.url;
238                 FM_BITCOIN_RPC_KIND = cfg.bitcoin.rpc.kind;
239               }
240               cfg.environment
241             ];
242             serviceConfig = {
243               DynamicUser = true;
245               StateDirectory = "fedimintd-${fedimintdName}";
246               StateDirectoryMode = "0700";
247               ExecStart = startScript;
249               Restart = "always";
250               RestartSec = 10;
251               StartLimitBurst = 5;
252               UMask = "007";
253               LimitNOFILE = "100000";
255               LockPersonality = true;
256               MemoryDenyWriteExecute = true;
257               NoNewPrivileges = true;
258               PrivateDevices = true;
259               PrivateMounts = true;
260               PrivateTmp = true;
261               ProtectClock = true;
262               ProtectControlGroups = true;
263               ProtectHostname = true;
264               ProtectKernelLogs = true;
265               ProtectKernelModules = true;
266               ProtectKernelTunables = true;
267               ProtectSystem = "full";
268               RestrictAddressFamilies = [
269                 "AF_INET"
270                 "AF_INET6"
271               ];
272               RestrictNamespaces = true;
273               RestrictRealtime = true;
274               SystemCallArchitectures = "native";
275               SystemCallFilter = [
276                 "@system-service"
277                 "~@privileged"
278               ];
279             };
280           }
281         ))
282       ) eachFedimintd;
284       services.nginx.virtualHosts = mapAttrs' (
285         fedimintdName: cfg:
286         (nameValuePair cfg.nginx.fqdn (
287           lib.mkMerge [
288             cfg.nginx.config
290             {
291               # Note: we want by default to enable OpenSSL, but it seems anything 100 and above is
292               # overriden by default value from vhost-options.nix
293               enableACME = mkOverride 99 true;
294               forceSSL = mkOverride 99 true;
295               locations.${cfg.nginx.path} = {
296                 proxyPass = "http://127.0.0.1:${toString cfg.api.port}/";
297                 proxyWebsockets = true;
298                 extraConfig = ''
299                   proxy_pass_header Authorization;
300                 '';
301               };
302             }
303           ]
304         ))
305       ) eachFedimintdNginx;
306     };
308   meta.maintainers = with lib.maintainers; [ dpc ];