joinmarket: init the jmwalletd service

This commit is contained in:
Otto Sabart 2024-05-13 21:00:00 +02:00
parent 8ec33ea1fd
commit 4d34ced501
No known key found for this signature in database
GPG Key ID: 823BAE99F8BE1E3C
6 changed files with 188 additions and 3 deletions

View File

@ -0,0 +1,163 @@
{ config, lib, pkgs, ... }:
with lib;
let
options.services.joinmarket-jmwalletd = {
enable = mkEnableOption "JoinMarket jmwalletd";
# Unfortunately it's not possible to set the listening address for
# jmwalletd. It's used only internally.
address = mkOption {
type = types.str;
readOnly = true;
internal = true;
default = "127.0.0.1";
description = mdDoc ''
The address where the jmwalletd listens to.
'';
};
port = mkOption {
type = types.port;
default = 28183;
description = mdDoc "The port over which to serve RPC.";
};
wssPort = mkOption {
type = types.port;
default = 28283;
description = mdDoc "The port over which to serve websocket subscriptions.";
};
extraArgs = mkOption {
type = types.separatedString " ";
default = "";
description = mdDoc "Extra coomand line arguments passed to jmwalletd.";
};
user = mkOption {
type = types.str;
default = "joinmarket-jmwalletd";
description = mdDoc "The user as which to run JoinMarket jmwalletd.";
};
group = mkOption {
type = types.str;
default = cfg.user;
description = mdDoc "The group as which to run JoinMarket jmwalletd.";
};
dataDir = mkOption {
readOnly = true;
type = types.path;
default = config.services.joinmarket.dataDir;
description = mdDoc "The JoinMarket data directory.";
};
sslDir = mkOption {
readOnly = true;
type = types.path;
default = "${cfg.dataDir}/ssl";
description = mdDoc "The SSL directory for jmwalled.";
};
certPath = mkOption {
readOnly = true;
default = "${secretsDir}/joinmarket-jmwalletd";
description = mdDoc "JoinMarket jmwalletd TLS certificate path.";
};
recoverSync = mkOption {
type = types.bool;
default = false;
description = mdDoc ''
Choose to do detailed wallet sync, used for recovering on new Core
instance.
'';
};
certificate = {
extraIPs = mkOption {
type = with types; listOf str;
default = [];
example = [ "60.100.0.1" ];
description = mdDoc ''
Extra `subjectAltName` IPs added to the certificate.
'';
};
extraDomains = mkOption {
type = with types; listOf str;
default = [];
example = [ "example.com" ];
description = mdDoc ''
Extra `subjectAltName` domain names added to the certificate.
'';
};
};
};
cfg = config.services.joinmarket-jmwalletd;
nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs;
secretsDir = config.nix-bitcoin.secretsDir;
in {
inherit options;
config = mkIf cfg.enable (mkMerge [{
services.joinmarket.enable = true;
users = {
users.${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = cfg.dataDir;
# Allow access to the joinmarket dataDir.
extraGroups = [ config.services.joinmarket.group ];
};
groups.${cfg.group} = {};
};
nix-bitcoin = {
secrets.joinmarket-jmwalletd-password.user = cfg.user;
generateSecretsCmds.joinmarket-jmwalletd-password = ''
makePasswordSecret joinmarket-jmwalletd-password
'';
};
}
(mkIf cfg.enable {
nix-bitcoin = {
secrets = {
joinmarket-jmwalletd-cert.user = cfg.user;
joinmarket-jmwalletd-key.user = cfg.user;
};
generateSecretsCmds.joinmarket-jmwalletd = ''
makeCert joinmarket-jmwalletd '${nbLib.mkCertExtraAltNames cfg.certificate}'
'';
};
systemd.tmpfiles.rules = [
"d '${cfg.sslDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
systemd.services.joinmarket-jmwalletd = {
wantedBy = [ "joinmarket.service" ];
requires = [ "joinmarket.service" ];
after = [ "joinmarket.service" "nix-bitcoin-secrets.target" ];
preStart = ''
# Copy the certificates into a data directory under the `ssl` dir
mkdir -p '${cfg.sslDir}'
install -m600 '${cfg.certPath}-cert' '${cfg.sslDir}/cert.pem'
install -m600 '${cfg.certPath}-key' '${cfg.sslDir}/key.pem'
'';
serviceConfig = nbLib.defaultHardening // {
WorkingDirectory = cfg.dataDir;
User = cfg.user;
ExecStart = ''
${config.nix-bitcoin.pkgs.joinmarket}/bin/jm-jmwalletd \
--port='${toString cfg.port}' \
--wss-port='${toString cfg.wssPort}' \
--datadir='${cfg.dataDir}' \
${optionalString (cfg.recoverSync) "--recoversync \\"}
${cfg.extraArgs}
'';
SyslogIdentifier = "joinmarket-jmwalletd";
ReadWritePaths = [ cfg.dataDir ];
Restart = "on-failure";
RestartSec = "10s";
MemoryDenyWriteExecute = false;
} // nbLib.allowTor;
};
})
]);
}

View File

@ -27,6 +27,7 @@
./btcpayserver.nix
./joinmarket.nix
./joinmarket-ob-watcher.nix
./joinmarket-jmwalletd.nix
./hardware-wallets.nix
# Support features

View File

@ -345,8 +345,14 @@ in {
messagingAddress = netns.joinmarket.address;
cliExec = mkCliExec "joinmarket";
};
systemd.services.joinmarket-yieldgenerator = mkIf config.services.joinmarket.yieldgenerator.enable {
serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket";
systemd.services = {
joinmarket-yieldgenerator = mkIf config.services.joinmarket.yieldgenerator.enable {
serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket";
};
joinmarket-jmwalletd = mkIf config.services.joinmarket-jmwalletd.enable {
serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket";
};
};
services.joinmarket-ob-watcher.address = netns.joinmarket-ob-watcher.address;

View File

@ -148,6 +148,7 @@ in {
btcpayserver = mkInfo "";
liquidd = mkInfo "";
joinmarket-ob-watcher = mkInfo "";
joinmarket-jmwalletd = mkInfo "";
rtl = mkInfo "";
mempool = mkInfo "";
mempool-frontend = name: cfg: mkInfoLong {

View File

@ -122,6 +122,8 @@ let
tests.joinmarket = cfg.joinmarket.enable;
tests.joinmarket-yieldgenerator = cfg.joinmarket.yieldgenerator.enable;
tests.joinmarket-jmwalletd = cfg.joinmarket-jmwalletd.enable;
tests.joinmarket-ob-watcher = cfg.joinmarket-ob-watcher.enable;
services.joinmarket.yieldgenerator = {
enable = config.services.joinmarket.enable;
@ -133,7 +135,6 @@ let
};
tests.nodeinfo = config.nix-bitcoin.nodeinfo.enable;
tests.backups = cfg.backups.enable;
# To test that unused secrets are made inaccessible by 'setup-secrets'
@ -205,6 +206,7 @@ let
services.btcpayserver.enable = true;
services.joinmarket.enable = true;
services.joinmarket-ob-watcher.enable = true;
services.joinmarket-jmwalletd.enable = true;
services.backups.enable = true;
nix-bitcoin.nodeinfo.enable = true;
@ -252,6 +254,7 @@ let
services.fulcrum.enable = true;
services.btcpayserver.enable = true;
services.joinmarket.enable = true;
services.joinmarket-jmwalletd.enable = true;
};
# netns and regtest, without secure-node.nix

View File

@ -281,6 +281,17 @@ def _():
assert_running("joinmarket-ob-watcher")
machine.wait_until_succeeds(log_has_string("joinmarket-ob-watcher", "Starting ob-watcher"))
@test("joinmarket-jmwalletd")
def _():
assert_running("joinmarket-jmwalletd")
machine.wait_until_succeeds(log_has_string("joinmarket-jmwalletd", "Started joinmarket-jmwalletd.service."))
machine.wait_until_succeeds(log_has_string("joinmarket-jmwalletd", "Starting jmwalletd on port: 28183"))
wait_for_open_port(ip("joinmarket"), 28183) # RPC port
wait_for_open_port(ip("joinmarket"), 28283) # Websocket SSL port
# Test web server response
assert_full_match(f"curl -fsSL --insecure https://{ip('joinmarket')}:28183/api/v1/getinfo | jq -jr keys[0]", "version")
@test("nodeinfo")
def _():
status, _ = machine.execute("systemctl is-enabled --quiet onion-addresses 2> /dev/null")