Skip to content

Commit

Permalink
joinmarket: init the jmwalletd service
Browse files Browse the repository at this point in the history
  • Loading branch information
seberm committed May 27, 2024
1 parent 8ec33ea commit b6454fd
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 3 deletions.
156 changes: 156 additions & 0 deletions modules/joinmarket-jmwalletd.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
{ 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 [{
# TODO: possible recursion?
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 ];
};
users.groups.${cfg.group} = {};

nix-bitcoin.secrets.joinmarket-jmwalletd-password.user = cfg.user;
nix-bitcoin.generateSecretsCmds.joinmarket-jmwalletd-password = ''
makePasswordSecret joinmarket-jmwalletd-password
'';
}

(mkIf cfg.enable {
nix-bitcoin.secrets.joinmarket-jmwalletd-cert.user = cfg.user;
nix-bitcoin.secrets.joinmarket-jmwalletd-key.user = cfg.user;
nix-bitcoin.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;
};
})
]);
}
1 change: 1 addition & 0 deletions modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
./btcpayserver.nix
./joinmarket.nix
./joinmarket-ob-watcher.nix
./joinmarket-jmwalletd.nix
./hardware-wallets.nix

# Support features
Expand Down
10 changes: 8 additions & 2 deletions modules/netns-isolation.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions modules/nodeinfo.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 3 additions & 1 deletion test/tests.nix
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,9 @@ let

tests.joinmarket = cfg.joinmarket.enable;
tests.joinmarket-yieldgenerator = cfg.joinmarket.yieldgenerator.enable;
tests.joinmarket-jmwalletd = cfg.joinmarket-jmwalletd.enable;
services.joinmarket-jmwalletd.enable = config.services.joinmarket.enable;

tests.joinmarket-ob-watcher = cfg.joinmarket-ob-watcher.enable;
services.joinmarket.yieldgenerator = {
enable = config.services.joinmarket.enable;
Expand All @@ -133,7 +136,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'
Expand Down
11 changes: 11 additions & 0 deletions test/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit b6454fd

Please sign in to comment.