nixos/fedimintd: init services
This commit is contained in:
parent
78389c563c
commit
ba727987d4
@ -163,6 +163,8 @@
|
|||||||
|
|
||||||
- [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).
|
- [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).
|
||||||
|
|
||||||
|
- [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).
|
||||||
|
|
||||||
## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
|
## Backward Incompatibilities {#sec-release-24.11-incompatibilities}
|
||||||
|
|
||||||
- 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.
|
- 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.
|
||||||
|
@ -1031,6 +1031,7 @@
|
|||||||
./services/networking/expressvpn.nix
|
./services/networking/expressvpn.nix
|
||||||
./services/networking/fakeroute.nix
|
./services/networking/fakeroute.nix
|
||||||
./services/networking/fastnetmon-advanced.nix
|
./services/networking/fastnetmon-advanced.nix
|
||||||
|
./services/networking/fedimintd.nix
|
||||||
./services/networking/ferm.nix
|
./services/networking/ferm.nix
|
||||||
./services/networking/firefox-syncserver.nix
|
./services/networking/firefox-syncserver.nix
|
||||||
./services/networking/fireqos.nix
|
./services/networking/fireqos.nix
|
||||||
|
304
nixos/modules/services/networking/fedimintd.nix
Normal file
304
nixos/modules/services/networking/fedimintd.nix
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
inherit (lib)
|
||||||
|
concatLists
|
||||||
|
filterAttrs
|
||||||
|
mapAttrs'
|
||||||
|
mapAttrsToList
|
||||||
|
mkEnableOption
|
||||||
|
mkIf
|
||||||
|
mkOption
|
||||||
|
mkOverride
|
||||||
|
mkPackageOption
|
||||||
|
nameValuePair
|
||||||
|
recursiveUpdate
|
||||||
|
types
|
||||||
|
;
|
||||||
|
|
||||||
|
fedimintdOpts =
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
name,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "fedimintd";
|
||||||
|
|
||||||
|
package = mkPackageOption pkgs "fedimint" { };
|
||||||
|
|
||||||
|
environment = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
description = "Extra Environment variables to pass to the fedimintd.";
|
||||||
|
default = {
|
||||||
|
RUST_BACKTRACE = "1";
|
||||||
|
};
|
||||||
|
example = {
|
||||||
|
RUST_LOG = "info,fm=debug";
|
||||||
|
RUST_BACKTRACE = "1";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
p2p = {
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Opens port in firewall for fedimintd's p2p port";
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 8173;
|
||||||
|
description = "Port to bind on for p2p connections from peers";
|
||||||
|
};
|
||||||
|
bind = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "0.0.0.0";
|
||||||
|
description = "Address to bind on for p2p connections from peers";
|
||||||
|
};
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "fedimint://p2p.myfedimint.com";
|
||||||
|
description = ''
|
||||||
|
Public address for p2p connections from peers
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
api = {
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Opens port in firewall for fedimintd's api port";
|
||||||
|
};
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 8174;
|
||||||
|
description = "Port to bind on for API connections relied by the reverse proxy/tls terminator.";
|
||||||
|
};
|
||||||
|
bind = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "127.0.0.1";
|
||||||
|
description = "Address to bind on for API connections relied by the reverse proxy/tls terminator.";
|
||||||
|
};
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = ''
|
||||||
|
Public URL of the API address of the reverse proxy/tls terminator. Usually starting with `wss://`.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
bitcoin = {
|
||||||
|
network = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "signet";
|
||||||
|
example = "bitcoin";
|
||||||
|
description = "Bitcoin network to participate in.";
|
||||||
|
};
|
||||||
|
rpc = {
|
||||||
|
url = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "http://127.0.0.1:38332";
|
||||||
|
example = "signet";
|
||||||
|
description = "Bitcoin node (bitcoind/electrum/esplora) address to connect to";
|
||||||
|
};
|
||||||
|
|
||||||
|
kind = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "bitcoind";
|
||||||
|
example = "electrum";
|
||||||
|
description = "Kind of a bitcoin node.";
|
||||||
|
};
|
||||||
|
|
||||||
|
secretFile = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
If set the URL specified in `bitcoin.rpc.url` will get the content of this file added
|
||||||
|
as an URL password, so `http://user@example.com` will turn into `http://user:SOMESECRET@example.com`.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
`/etc/nix-bitcoin-secrets/bitcoin-rpcpassword-public` (for nix-bitcoin default)
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
consensus.finalityDelay = mkOption {
|
||||||
|
type = types.ints.unsigned;
|
||||||
|
default = 10;
|
||||||
|
description = "Consensus peg-in finality delay.";
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/fedimintd-${name}/";
|
||||||
|
readOnly = true;
|
||||||
|
description = ''
|
||||||
|
Path to the data dir fedimintd will use to store its data.
|
||||||
|
Note that due to using the DynamicUser feature of systemd, this value should not be changed
|
||||||
|
and is set to be read only.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
nginx = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Whether to configure nginx for fedimintd
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
fqdn = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
example = "api.myfedimint.com";
|
||||||
|
description = "Public domain of the API address of the reverse proxy/tls terminator.";
|
||||||
|
};
|
||||||
|
config = mkOption {
|
||||||
|
type = types.submodule (
|
||||||
|
recursiveUpdate (import ../web-servers/nginx/vhost-options.nix {
|
||||||
|
inherit config lib;
|
||||||
|
}) { }
|
||||||
|
);
|
||||||
|
default = { };
|
||||||
|
description = "Overrides to the nginx vhost section for api";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options = {
|
||||||
|
services.fedimintd = mkOption {
|
||||||
|
type = types.attrsOf (types.submodule fedimintdOpts);
|
||||||
|
default = { };
|
||||||
|
description = "Specification of one or more fedimintd instances.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config =
|
||||||
|
let
|
||||||
|
eachFedimintd = filterAttrs (fedimintdName: cfg: cfg.enable) config.services.fedimintd;
|
||||||
|
eachFedimintdNginx = filterAttrs (fedimintdName: cfg: cfg.nginx.enable) eachFedimintd;
|
||||||
|
in
|
||||||
|
mkIf (eachFedimintd != { }) {
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = concatLists (
|
||||||
|
mapAttrsToList (
|
||||||
|
fedimintdName: cfg:
|
||||||
|
(lib.optional cfg.api.openFirewall cfg.api.port ++ lib.optional cfg.p2p.openFirewall cfg.p2p.port)
|
||||||
|
) eachFedimintd
|
||||||
|
);
|
||||||
|
|
||||||
|
systemd.services = mapAttrs' (
|
||||||
|
fedimintdName: cfg:
|
||||||
|
(nameValuePair "fedimintd-${fedimintdName}" (
|
||||||
|
let
|
||||||
|
startScript = pkgs.writeShellScript "fedimintd-start" (
|
||||||
|
(
|
||||||
|
if cfg.bitcoin.rpc.secretFile != null then
|
||||||
|
''
|
||||||
|
secret=$(${pkgs.coreutils}/bin/head -n 1 "${cfg.bitcoin.rpc.secretFile}")
|
||||||
|
prefix="''${FM_BITCOIN_RPC_URL%*@*}" # Everything before the last '@'
|
||||||
|
suffix="''${FM_BITCOIN_RPC_URL##*@}" # Everything after the last '@'
|
||||||
|
FM_BITCOIN_RPC_URL="''${prefix}:''${secret}@''${suffix}"
|
||||||
|
''
|
||||||
|
else
|
||||||
|
""
|
||||||
|
)
|
||||||
|
+ ''
|
||||||
|
exec ${cfg.package}/bin/fedimintd
|
||||||
|
''
|
||||||
|
);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
description = "Fedimint Server";
|
||||||
|
documentation = [ "https://github.com/fedimint/fedimint/" ];
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
environment = lib.mkMerge [
|
||||||
|
{
|
||||||
|
FM_BIND_P2P = "${cfg.p2p.bind}:${toString cfg.p2p.port}";
|
||||||
|
FM_BIND_API = "${cfg.api.bind}:${toString cfg.api.port}";
|
||||||
|
FM_P2P_URL = cfg.p2p.url;
|
||||||
|
FM_API_URL = cfg.api.url;
|
||||||
|
FM_DATA_DIR = cfg.dataDir;
|
||||||
|
FM_BITCOIN_NETWORK = cfg.bitcoin.network;
|
||||||
|
FM_BITCOIN_RPC_URL = cfg.bitcoin.rpc.url;
|
||||||
|
FM_BITCOIN_RPC_KIND = cfg.bitcoin.rpc.kind;
|
||||||
|
}
|
||||||
|
cfg.environment
|
||||||
|
];
|
||||||
|
serviceConfig = {
|
||||||
|
DynamicUser = true;
|
||||||
|
|
||||||
|
StateDirectory = "fedimintd-${fedimintdName}";
|
||||||
|
StateDirectoryMode = "0700";
|
||||||
|
ExecStart = startScript;
|
||||||
|
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 10;
|
||||||
|
StartLimitBurst = 5;
|
||||||
|
UMask = "007";
|
||||||
|
LimitNOFILE = "100000";
|
||||||
|
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = true;
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
PrivateMounts = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectClock = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
ProtectHostname = true;
|
||||||
|
ProtectKernelLogs = true;
|
||||||
|
ProtectKernelModules = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectSystem = "full";
|
||||||
|
RestrictAddressFamilies = [
|
||||||
|
"AF_INET"
|
||||||
|
"AF_INET6"
|
||||||
|
];
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
RestrictRealtime = true;
|
||||||
|
SystemCallArchitectures = "native";
|
||||||
|
SystemCallFilter = [
|
||||||
|
"@system-service"
|
||||||
|
"~@privileged"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
))
|
||||||
|
) eachFedimintd;
|
||||||
|
|
||||||
|
services.nginx.virtualHosts = mapAttrs' (
|
||||||
|
fedimintdName: cfg:
|
||||||
|
(nameValuePair cfg.nginx.fqdn (
|
||||||
|
lib.mkMerge [
|
||||||
|
cfg.nginx.config
|
||||||
|
|
||||||
|
{
|
||||||
|
# Note: we want by default to enable OpenSSL, but it seems anything 100 and above is
|
||||||
|
# overriden by default value from vhost-options.nix
|
||||||
|
enableACME = mkOverride 99 true;
|
||||||
|
forceSSL = mkOverride 99 true;
|
||||||
|
# Currently Fedimint API only support JsonRPC on `/ws/` endpoint, so no need to handle `/`
|
||||||
|
locations."/ws/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:${toString cfg.api.port}/";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
extraConfig = ''
|
||||||
|
proxy_pass_header Authorization;
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
]
|
||||||
|
))
|
||||||
|
) eachFedimintdNginx;
|
||||||
|
};
|
||||||
|
|
||||||
|
meta.maintainers = with lib.maintainers; [ dpc ];
|
||||||
|
}
|
@ -322,6 +322,7 @@ in {
|
|||||||
fancontrol = handleTest ./fancontrol.nix {};
|
fancontrol = handleTest ./fancontrol.nix {};
|
||||||
fanout = handleTest ./fanout.nix {};
|
fanout = handleTest ./fanout.nix {};
|
||||||
fcitx5 = handleTest ./fcitx5 {};
|
fcitx5 = handleTest ./fcitx5 {};
|
||||||
|
fedimintd = runTest ./fedimintd.nix;
|
||||||
fenics = handleTest ./fenics.nix {};
|
fenics = handleTest ./fenics.nix {};
|
||||||
ferm = handleTest ./ferm.nix {};
|
ferm = handleTest ./ferm.nix {};
|
||||||
ferretdb = handleTest ./ferretdb.nix {};
|
ferretdb = handleTest ./ferretdb.nix {};
|
||||||
|
37
nixos/tests/fedimintd.nix
Normal file
37
nixos/tests/fedimintd.nix
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
# This test runs the fedimintd and verifies that it starts
|
||||||
|
|
||||||
|
{ pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
name = "fedimintd";
|
||||||
|
|
||||||
|
meta = with pkgs.lib.maintainers; {
|
||||||
|
maintainers = [ dpc ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes.machine =
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.fedimintd."mainnet" = {
|
||||||
|
enable = true;
|
||||||
|
p2p = {
|
||||||
|
url = "fedimint://example.com";
|
||||||
|
};
|
||||||
|
api = {
|
||||||
|
url = "wss://example.com";
|
||||||
|
};
|
||||||
|
environment = {
|
||||||
|
"FM_REL_NOTES_ACK" = "0_4_xyz";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript =
|
||||||
|
{ nodes, ... }:
|
||||||
|
''
|
||||||
|
start_all()
|
||||||
|
|
||||||
|
machine.wait_for_unit("fedimintd-mainnet.service")
|
||||||
|
machine.wait_for_open_port(${toString nodes.machine.services.fedimintd.mainnet.api.port})
|
||||||
|
'';
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user