Merge #312: Refactorings, cleanups

0a2c8e4864 run-tests: add option --copy-src (Erik Arvstedt)
803584a288 backups: don't use hardcoded secrets dir (Erik Arvstedt)
c29d44b49a ci: use 'cachix watch-exec' (Erik Arvstedt)
6a32812412 services: add names for systemd helper scripts (Erik Arvstedt)
6982699613 services: use consistent layout (Erik Arvstedt)
a43534dda0 services: improve config file setup (Erik Arvstedt)
18f2002cf0 joinmarket-yieldgenerator: improve systemd journal output (Erik Arvstedt)
9d0b8c8f6f joinmarket-ob-watcher: use DynamicUser (Erik Arvstedt)
e9c98f415c joinmarket: explain need for tor control socket (Erik Arvstedt)
d9c87b6a8f joinmarket: fix wallet creation (Erik Arvstedt)
7458350108 treewide: remove deprecated types.loaOf (Erik Arvstedt)
9cf038939c treewide: use mkEnableOption (Erik Arvstedt)
7a97304f13 treewide: remove unit descriptions (Erik Arvstedt)
a942177ecf treewide: remove user descriptions (Erik Arvstedt)
4f6ff408ef treewide: remove unneeded string literals (Erik Arvstedt)
e6a6c721c1 treewide: streamline 'extraConfig' descriptions (Erik Arvstedt)
e774c045de treewide: fix formatting (Erik Arvstedt)
0b5b29a2a3 netns-isolation: simplify permission definition for netns-exec (Erik Arvstedt)
a587a2b02a defaultHardening: explain where @system-service is defined (Erik Arvstedt)
bb3a69797e README: minor improvements (Erik Arvstedt)
13fc9dfabf examples: improve introductory comments (Erik Arvstedt)
af2040f4c4 netns-isolation: use 'true' for systemd option (Erik Arvstedt)
c246bbb36e bitcoind, clightning, lnd: improve descriptions (Erik Arvstedt)
7533f12ef1 bitcoind, clightning, run-tests: minor refactoring (Erik Arvstedt)
41fe9b0c1d elementsd: minor refactoring (Erik Arvstedt)
f0850d3f23 btcpayserver: reorder config settings (Erik Arvstedt)
d1c0ea9f85 btcpayserver: add missing systemd postgresql dependency (Erik Arvstedt)

Pull request description:

ACKs for top commit:
  jonasnick:
    ACK 0a2c8e4864

Tree-SHA512: 5c81b36042fbb2f016c8e58ba9e05ef3389d5376b8df713d3258d2cd0b6a9239904531171aca8e49bea7039341d5fa91aa9474c6d98de849c25ede52deccc5a3
This commit is contained in:
Jonas Nick 2021-02-08 20:31:56 +00:00
commit f9683889d9
No known key found for this signature in database
GPG Key ID: 4861DBF262123605
25 changed files with 236 additions and 263 deletions

View File

@ -43,7 +43,7 @@ running nix-bitcoin does not require any previous experience with the Nix ecosys
Examples Examples
--- ---
See the [examples directory](examples/README.md). See [here for examples](examples/README.md).
Features Features
--- ---
@ -76,7 +76,7 @@ NixOS modules
* Helper * Helper
* [netns-isolation](modules/netns-isolation.nix): isolates applications on the network-level via network namespaces * [netns-isolation](modules/netns-isolation.nix): isolates applications on the network-level via network namespaces
* [nodeinfo](modules/nodeinfo.nix): script which prints info about the node's services * [nodeinfo](modules/nodeinfo.nix): script which prints info about the node's services
* [backups](modules/backups.nix): daily duplicity backups of all your node's important files * [backups](modules/backups.nix): duplicity backups of all your node's important files
* [operator](modules/operator.nix): adds non-root user `operator` who has access to client tools (e.g. `bitcoin-cli`, `lightning-cli`) * [operator](modules/operator.nix): adds non-root user `operator` who has access to client tools (e.g. `bitcoin-cli`, `lightning-cli`)
Security Security

View File

@ -11,18 +11,8 @@ cachixCache=nix-bitcoin
trap 'echo Error at line $LINENO' ERR trap 'echo Error at line $LINENO' ERR
atExit() {
rm -rf $tmpDir
if [[ -v cachixPid ]]; then stopCachix; fi
}
tmpDir=$(mktemp -d -p /tmp) tmpDir=$(mktemp -d -p /tmp)
trap atExit EXIT trap "rm -rf $tmpDir" EXIT
stopCachix() {
kill $cachixPid 2>/dev/null || true
# Wait for cachix to finish
tail --pid=$cachixPid -f /dev/null
}
## Instantiate ## Instantiate
@ -43,14 +33,14 @@ fi
if [[ $CACHIX_SIGNING_KEY ]]; then if [[ $CACHIX_SIGNING_KEY ]]; then
# Speed up task by uploading store paths as soon as they are created # Speed up task by uploading store paths as soon as they are created
cachix push $cachixCache --watch-store & buildCmd="cachix watch-exec $cachixCache nix-build --"
cachixPid=$! else
buildCmd=nix-build
fi fi
nix-build --out-link $tmpDir/result $tmpDir/drv >/dev/null $buildCmd --out-link $tmpDir/result $tmpDir/drv >/dev/null
if [[ $CACHIX_SIGNING_KEY ]]; then if [[ $CACHIX_SIGNING_KEY ]]; then
stopCachix
cachix push $cachixCache $outPath cachix push $cachixCache $outPath
fi fi

View File

@ -5,8 +5,8 @@ set -euo pipefail
# Running this script leaves no traces on your host system. # Running this script leaves no traces on your host system.
# This demo is a template for your own experiments. # This demo is a template for your own experiments.
# Feel free to modify or to run nix-shell and execute individual statements of this # Run with option `--interactive` or `-i` to start a shell for interacting with
# script in the interactive shell. # the node.
if [[ ! -v IN_NIX_SHELL ]]; then if [[ ! -v IN_NIX_SHELL ]]; then
echo "Running script in nix shell env..." echo "Running script in nix shell env..."

View File

@ -5,8 +5,8 @@ set -euo pipefail
# Running this script leaves no traces on your host system. # Running this script leaves no traces on your host system.
# This demo is a template for your own experiments. # This demo is a template for your own experiments.
# Feel free to modify or to run nix-shell and execute individual statements of this # Run with option `--interactive` or `-i` to start a shell for interacting with
# script in the interactive shell. # the node.
if [[ ! -v IN_NIX_SHELL ]]; then if [[ ! -v IN_NIX_SHELL ]]; then
echo "Running script in nix shell env..." echo "Running script in nix shell env..."

View File

@ -5,8 +5,8 @@ set -euo pipefail
# Running this script leaves no traces on your host system. # Running this script leaves no traces on your host system.
# This demo is a template for your own experiments. # This demo is a template for your own experiments.
# Feel free to modify or to run nix-shell and execute individual statements of this # Run with option `--interactive` or `-i` to start a shell for interacting with
# script in the interactive shell. # the node.
# MAKE SURE TO REPLACE the SSH identity file if you use this script for # MAKE SURE TO REPLACE the SSH identity file if you use this script for
# anything serious. # anything serious.

View File

@ -4,13 +4,15 @@ with lib;
let let
cfg = config.services.backups; cfg = config.services.backups;
secretsDir = config.nix-bitcoin.secretsDir;
filelist = pkgs.writeText "filelist.txt" '' filelist = pkgs.writeText "filelist.txt" ''
${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/blocks"} ${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/blocks"}
${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/chainstate"} ${optionalString (!cfg.with-bulk-data) "- ${config.services.bitcoind.dataDir}/chainstate"}
${config.services.bitcoind.dataDir} ${config.services.bitcoind.dataDir}
${config.services.clightning.dataDir} ${config.services.clightning.dataDir}
${config.services.lnd.dataDir} ${config.services.lnd.dataDir}
/secrets/lnd-seed-mnemonic ${secretsDir}/lnd-seed-mnemonic
${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/blocks"} ${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/blocks"}
${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/chainstate"} ${optionalString (!cfg.with-bulk-data) "- ${config.services.liquidd.dataDir}/*/chainstate"}
${config.services.liquidd.dataDir} ${config.services.liquidd.dataDir}
@ -18,7 +20,7 @@ let
${config.services.nbxplorer.dataDir} ${config.services.nbxplorer.dataDir}
${config.services.btcpayserver.dataDir} ${config.services.btcpayserver.dataDir}
${config.services.joinmarket.dataDir} ${config.services.joinmarket.dataDir}
/secrets/jm-wallet-seed ${secretsDir}/jm-wallet-seed
${config.services.postgresqlBackup.location}/btcpaydb.sql.gz ${config.services.postgresqlBackup.location}/btcpaydb.sql.gz
/var/lib/tor /var/lib/tor
# Extra files # Extra files
@ -27,7 +29,6 @@ let
# Exclude all unspecified files and directories # Exclude all unspecified files and directories
- / - /
''; '';
in { in {
options.services.backups = { options.services.backups = {
enable = mkEnableOption "Backups service"; enable = mkEnableOption "Backups service";
@ -72,7 +73,7 @@ in {
"--include-filelist" "${filelist}" "--include-filelist" "${filelist}"
"--full-if-older-than" "1M" "--full-if-older-than" "1M"
]; ];
targetUrl = "${cfg.destination}"; targetUrl = cfg.destination;
frequency = cfg.frequency; frequency = cfg.frequency;
secretFile = "${config.nix-bitcoin.secretsDir}/backup-encryption-env"; secretFile = "${config.nix-bitcoin.secretsDir}/backup-encryption-env";
}; };

View File

@ -58,5 +58,5 @@
"validateaddress" "validateaddress"
"verifymessage" "verifymessage"
# Zmq # Zmq
"getzmqnotifications" "getzmqnotifications"
] ]

View File

@ -7,7 +7,7 @@ let
nbLib = config.nix-bitcoin.lib; nbLib = config.nix-bitcoin.lib;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
configFile = pkgs.writeText "bitcoin.conf" '' configFile = builtins.toFile "bitcoin.conf" ''
# We're already logging via journald # We're already logging via journald
nodebuglogfile=1 nodebuglogfile=1
@ -90,7 +90,7 @@ in {
par=16 par=16
logips=1 logips=1
''; '';
description = "Additional configurations to be appended to <filename>bitcoin.conf</filename>."; description = "Extra lines appended to <filename>bitcoin.conf</filename>.";
}; };
dataDir = mkOption { dataDir = mkOption {
type = types.path; type = types.path;
@ -138,7 +138,7 @@ in {
alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae"; alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99"; bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
}; };
type = with types; loaOf (submodule ({ name, ... }: { type = with types; attrsOf (submodule ({ name, ... }: {
options = { options = {
name = mkOption { name = mkOption {
type = types.str; type = types.str;
@ -197,9 +197,7 @@ in {
listen = mkOption { listen = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = "Accept incoming connections.";
If enabled, the bitcoin service will listen.
'';
}; };
dataDirReadableByGroup = mkOption { dataDirReadableByGroup = mkOption {
type = types.bool; type = types.bool;
@ -228,21 +226,15 @@ in {
type = types.nullOr (types.ints.between 4 16384); type = types.nullOr (types.ints.between 4 16384);
default = null; default = null;
example = 4000; example = 4000;
description = "Override the default database cache size in megabytes."; description = "Override the default database cache size in MiB.";
}; };
prune = mkOption { prune = mkOption {
type = types.ints.unsigned; type = types.ints.unsigned;
default = 0; default = 0;
example = 10000; example = 10000;
description = '' description = ''
Reduce storage requirements by enabling pruning (deleting) of old Automatically prune block files to stay under the specified target size in MiB.
blocks. This allows the pruneblockchain RPC to be called to delete Value 0 disables pruning.
specific blocks, and enables automatic pruning of old blocks if a
target size in MiB is provided. This mode is incompatible with -txindex
and -rescan. Warning: Reverting this setting requires re-downloading
the entire blockchain. ("disable" = disable pruning blocks, "manual"
= allow manual pruning via RPC, >=550 = automatically prune block files
to stay under the specified target size in MiB)
''; '';
}; };
zmqpubrawblock = mkOption { zmqpubrawblock = mkOption {
@ -281,7 +273,7 @@ in {
type = types.nullOr types.str; type = types.nullOr types.str;
default = null; default = null;
example = "bech32"; example = "bech32";
description = "What type of addresses to use"; description = "The type of addresses to use";
}; };
cli = mkOption { cli = mkOption {
readOnly = true; readOnly = true;
@ -320,7 +312,6 @@ in {
]; ];
systemd.services.bitcoind = { systemd.services.bitcoind = {
description = "Bitcoin daemon";
requires = [ "nix-bitcoin-secrets.target" ]; requires = [ "nix-bitcoin-secrets.target" ];
after = [ "network.target" "nix-bitcoin-secrets.target" ]; after = [ "network.target" "nix-bitcoin-secrets.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
@ -334,10 +325,10 @@ in {
in '' in ''
${optionalString cfg.dataDirReadableByGroup "chmod -R g+rX '${cfg.dataDir}/blocks'"} ${optionalString cfg.dataDirReadableByGroup "chmod -R g+rX '${cfg.dataDir}/blocks'"}
cfg=$( cfg=$(
cat ${configFile}; cat ${configFile}
${extraRpcauth} ${extraRpcauth}
${/* Enable bitcoin-cli for group 'bitcoin' */ ""} ${/* Enable bitcoin-cli for group 'bitcoin' */ ""}
printf "rpcuser=${cfg.rpc.users.privileged.name}\nrpcpassword="; cat "${secretsDir}/bitcoin-rpcpassword-privileged"; printf "rpcuser=${cfg.rpc.users.privileged.name}\nrpcpassword="; cat "${secretsDir}/bitcoin-rpcpassword-privileged"
echo echo
${optionalString (cfg.getPublicAddressCmd != "") '' ${optionalString (cfg.getPublicAddressCmd != "") ''
echo "externalip=$(${cfg.getPublicAddressCmd})" echo "externalip=$(${cfg.getPublicAddressCmd})"
@ -351,13 +342,13 @@ in {
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
Type = "notify"; Type = "notify";
NotifyAccess = "all"; NotifyAccess = "all";
User = "${cfg.user}"; User = cfg.user;
Group = "${cfg.group}"; Group = cfg.group;
TimeoutStartSec = 300; TimeoutStartSec = 300;
ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'"; ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'";
Restart = "on-failure"; Restart = "on-failure";
UMask = mkIf cfg.dataDirReadableByGroup "0027"; UMask = mkIf cfg.dataDirReadableByGroup "0027";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nbLib.allowTor then nbLib.allowTor
else nbLib.allowAnyIP) else nbLib.allowAnyIP)
@ -383,16 +374,13 @@ in {
done done
''; '';
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
User = "${cfg.user}"; User = cfg.user;
Group = "${cfg.group}"; Group = cfg.group;
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
} // nbLib.allowTor; } // nbLib.allowTor;
}; };
users.users.${cfg.user} = { users.users.${cfg.user}.group = cfg.group;
group = cfg.group;
description = "Bitcoin daemon user";
};
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = {};
users.groups.bitcoinrpc = {}; users.groups.bitcoinrpc = {};
nix-bitcoin.operator.groups = [ cfg.group ]; nix-bitcoin.operator.groups = [ cfg.group ];

View File

@ -99,20 +99,34 @@ in {
services.clightning.enable = mkIf (cfg.btcpayserver.lightningBackend == "clightning") true; services.clightning.enable = mkIf (cfg.btcpayserver.lightningBackend == "clightning") true;
services.lnd.enable = mkIf (cfg.btcpayserver.lightningBackend == "lnd") true; services.lnd.enable = mkIf (cfg.btcpayserver.lightningBackend == "lnd") true;
systemd.tmpfiles.rules = [ services.bitcoind.rpc.users.btcpayserver = {
"d '${cfg.nbxplorer.dataDir}' 0770 ${cfg.nbxplorer.user} ${cfg.nbxplorer.group} - -" passwordHMACFromFile = true;
"d '${cfg.btcpayserver.dataDir}' 0770 ${cfg.btcpayserver.user} ${cfg.btcpayserver.group} - -" rpcwhitelist = cfg.bitcoind.rpc.users.public.rpcwhitelist ++ [
]; "setban"
"generatetoaddress"
"getpeerinfo"
];
};
services.lnd.macaroons.btcpayserver = mkIf (cfg.btcpayserver.lightningBackend == "lnd") {
inherit (cfg.btcpayserver) user;
permissions = ''{"entity":"info","action":"read"},{"entity":"onchain","action":"read"},{"entity":"offchain","action":"read"},{"entity":"address","action":"read"},{"entity":"message","action":"read"},{"entity":"peers","action":"read"},{"entity":"signer","action":"read"},{"entity":"invoices","action":"read"},{"entity":"invoices","action":"write"},{"entity":"address","action":"write"}'';
};
services.postgresql = { services.postgresql = {
enable = true; enable = true;
ensureDatabases = [ "btcpaydb" ]; ensureDatabases = [ "btcpaydb" ];
ensureUsers = [{ ensureUsers = [{
name = "${cfg.btcpayserver.user}"; name = cfg.btcpayserver.user;
ensurePermissions."DATABASE btcpaydb" = "ALL PRIVILEGES"; ensurePermissions."DATABASE btcpaydb" = "ALL PRIVILEGES";
}]; }];
}; };
systemd.tmpfiles.rules = [
"d '${cfg.nbxplorer.dataDir}' 0770 ${cfg.nbxplorer.user} ${cfg.nbxplorer.group} - -"
"d '${cfg.btcpayserver.dataDir}' 0770 ${cfg.btcpayserver.user} ${cfg.btcpayserver.group} - -"
];
systemd.services.nbxplorer = let systemd.services.nbxplorer = let
configFile = builtins.toFile "config" '' configFile = builtins.toFile "config" ''
network=${config.services.bitcoind.network} network=${config.services.bitcoind.network}
@ -123,12 +137,11 @@ in {
port=${toString cfg.nbxplorer.port} port=${toString cfg.nbxplorer.port}
''; '';
in { in {
description = "Run nbxplorer";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
preStart = '' preStart = ''
install -m 600 ${configFile} ${cfg.nbxplorer.dataDir}/settings.config install -m 600 ${configFile} '${cfg.nbxplorer.dataDir}/settings.config'
echo "btcrpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-btcpayserver)" \ echo "btcrpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-btcpayserver)" \
>> '${cfg.nbxplorer.dataDir}/settings.config' >> '${cfg.nbxplorer.dataDir}/settings.config'
''; '';
@ -151,13 +164,13 @@ in {
systemd.services.btcpayserver = let systemd.services.btcpayserver = let
configFile = builtins.toFile "config" ('' configFile = builtins.toFile "config" (''
network=${config.services.bitcoind.network} network=${config.services.bitcoind.network}
postgres=User ID=${cfg.btcpayserver.user};Host=/run/postgresql;Database=btcpaydb bind=${cfg.btcpayserver.address}
port=${toString cfg.btcpayserver.port}
socksendpoint=${cfg.tor.client.socksListenAddress} socksendpoint=${cfg.tor.client.socksListenAddress}
btcexplorerurl=http://${cfg.nbxplorer.address}:${toString cfg.nbxplorer.port}/ btcexplorerurl=http://${cfg.nbxplorer.address}:${toString cfg.nbxplorer.port}/
btcexplorercookiefile=${cfg.nbxplorer.dataDir}/${config.services.bitcoind.makeNetworkName "Main" "RegTest"}/.cookie btcexplorercookiefile=${cfg.nbxplorer.dataDir}/${config.services.bitcoind.makeNetworkName "Main" "RegTest"}/.cookie
bind=${cfg.btcpayserver.address} postgres=User ID=${cfg.btcpayserver.user};Host=/run/postgresql;Database=btcpaydb
${optionalString (cfg.btcpayserver.rootpath != null) "rootpath=${cfg.btcpayserver.rootpath}"} ${optionalString (cfg.btcpayserver.rootpath != null) "rootpath=${cfg.btcpayserver.rootpath}"}
port=${toString cfg.btcpayserver.port}
'' + optionalString (cfg.btcpayserver.lightningBackend == "clightning") '' '' + optionalString (cfg.btcpayserver.lightningBackend == "clightning") ''
btclightning=type=clightning;server=unix:///${cfg.clightning.dataDir}/bitcoin/lightning-rpc btclightning=type=clightning;server=unix:///${cfg.clightning.dataDir}/bitcoin/lightning-rpc
''); '');
@ -168,17 +181,17 @@ in {
"certthumbprint="; "certthumbprint=";
in let self = { in let self = {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "nbxplorer.service" ] requires = [ "nbxplorer.service" "postgresql.service" ]
++ optional (cfg.btcpayserver.lightningBackend != null) "${cfg.btcpayserver.lightningBackend}.service"; ++ optional (cfg.btcpayserver.lightningBackend != null) "${cfg.btcpayserver.lightningBackend}.service";
after = self.requires; after = self.requires;
preStart = '' preStart = ''
install -m 600 ${configFile} ${cfg.btcpayserver.dataDir}/settings.config install -m 600 ${configFile} '${cfg.btcpayserver.dataDir}/settings.config'
${optionalString (cfg.btcpayserver.lightningBackend == "lnd") '' ${optionalString (cfg.btcpayserver.lightningBackend == "lnd") ''
{ {
echo -n "${lndConfig}"; echo -n "${lndConfig}";
${pkgs.openssl}/bin/openssl x509 -noout -fingerprint -sha256 -in ${config.nix-bitcoin.secretsDir}/lnd-cert \ ${pkgs.openssl}/bin/openssl x509 -noout -fingerprint -sha256 -in ${config.nix-bitcoin.secretsDir}/lnd-cert \
| sed -e 's/.*=//;s/://g'; | sed -e 's/.*=//;s/://g';
} >> ${cfg.btcpayserver.dataDir}/settings.config } >> '${cfg.btcpayserver.dataDir}/settings.config'
''} ''}
''; '';
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
@ -197,20 +210,13 @@ in {
); );
}; in self; }; in self;
services.lnd.macaroons.btcpayserver = mkIf (cfg.btcpayserver.lightningBackend == "lnd") {
inherit (cfg.btcpayserver) user;
permissions = ''{"entity":"info","action":"read"},{"entity":"onchain","action":"read"},{"entity":"offchain","action":"read"},{"entity":"address","action":"read"},{"entity":"message","action":"read"},{"entity":"peers","action":"read"},{"entity":"signer","action":"read"},{"entity":"invoices","action":"read"},{"entity":"invoices","action":"write"},{"entity":"address","action":"write"}'';
};
users.users.${cfg.nbxplorer.user} = { users.users.${cfg.nbxplorer.user} = {
description = "nbxplorer user";
group = cfg.nbxplorer.group; group = cfg.nbxplorer.group;
extraGroups = [ "bitcoinrpc" ]; extraGroups = [ "bitcoinrpc" ];
home = cfg.nbxplorer.dataDir; home = cfg.nbxplorer.dataDir;
}; };
users.groups.${cfg.nbxplorer.group} = {}; users.groups.${cfg.nbxplorer.group} = {};
users.users.${cfg.btcpayserver.user} = { users.users.${cfg.btcpayserver.user} = {
description = "btcpayserver user";
group = cfg.btcpayserver.group; group = cfg.btcpayserver.group;
extraGroups = [ "nbxplorer" ] extraGroups = [ "nbxplorer" ]
++ optional (cfg.btcpayserver.lightningBackend == "clightning") cfg.clightning.user; ++ optional (cfg.btcpayserver.lightningBackend == "clightning") cfg.clightning.user;
@ -218,18 +224,12 @@ in {
}; };
users.groups.${cfg.btcpayserver.group} = {}; users.groups.${cfg.btcpayserver.group} = {};
services.bitcoind.rpc.users.btcpayserver = { nix-bitcoin.secrets = {
passwordHMACFromFile = true; bitcoin-rpcpassword-btcpayserver = {
rpcwhitelist = cfg.bitcoind.rpc.users.public.rpcwhitelist ++ [ user = "bitcoin";
"setban" group = "nbxplorer";
"generatetoaddress" };
"getpeerinfo" bitcoin-HMAC-btcpayserver.user = "bitcoin";
];
}; };
nix-bitcoin.secrets.bitcoin-rpcpassword-btcpayserver = {
user = "bitcoin";
group = "nbxplorer";
};
nix-bitcoin.secrets.bitcoin-HMAC-btcpayserver.user = "bitcoin";
}; };
} }

View File

@ -11,7 +11,7 @@ let
network=${network} network=${network}
bitcoin-datadir=${config.services.bitcoind.dataDir} bitcoin-datadir=${config.services.bitcoind.dataDir}
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"} ${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
always-use-proxy=${if cfg.always-use-proxy then "true" else "false"} always-use-proxy=${boolToString cfg.always-use-proxy}
bind-addr=${cfg.address}:${toString cfg.port} bind-addr=${cfg.address}:${toString cfg.port}
bitcoin-rpcconnect=${config.services.bitcoind.rpc.address} bitcoin-rpcconnect=${config.services.bitcoind.rpc.address}
bitcoin-rpcport=${toString config.services.bitcoind.rpc.port} bitcoin-rpcport=${toString config.services.bitcoind.rpc.port}
@ -21,13 +21,7 @@ let
''; '';
in { in {
options.services.clightning = { options.services.clightning = {
enable = mkOption { enable = mkEnableOption "clightning";
type = types.bool;
default = false;
description = ''
If enabled, the clightning service will be installed.
'';
};
address = mkOption { address = mkOption {
type = types.str; type = types.str;
default = "127.0.0.1"; default = "127.0.0.1";
@ -41,13 +35,17 @@ in {
proxy = mkOption { proxy = mkOption {
type = types.nullOr types.str; type = types.nullOr types.str;
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null; default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
description = "Set a socks proxy to use to connect to Tor nodes (or for all connections if *always-use-proxy* is set)"; description = ''
Socks proxy for connecting to Tor nodes (or for all connections if option always-use-proxy is set).
'';
}; };
always-use-proxy = mkOption { always-use-proxy = mkOption {
type = types.bool; type = types.bool;
default = cfg.enforceTor; default = cfg.enforceTor;
description = '' description = ''
Always use the *proxy*, even to connect to normal IP addresses (you can still connect to Unix domain sockets manually). This also disables all DNS lookups, to avoid leaking information. Always use the proxy, even to connect to normal IP addresses.
You can still connect to Unix domain sockets manually.
This also disables all DNS lookups, to avoid leaking address information.
''; '';
}; };
dataDir = mkOption { dataDir = mkOption {
@ -63,7 +61,7 @@ in {
extraConfig = mkOption { extraConfig = mkOption {
type = types.lines; type = types.lines;
default = ""; default = "";
description = "Additional lines appended to the config file."; description = "Extra lines appended to the configuration file.";
}; };
user = mkOption { user = mkOption {
type = types.str; type = types.str;
@ -77,8 +75,7 @@ in {
}; };
cli = mkOption { cli = mkOption {
readOnly = true; readOnly = true;
default = pkgs.writeScriptBin "lightning-cli" default = pkgs.writeScriptBin "lightning-cli" ''
''
${nbPkgs.clightning}/bin/lightning-cli --lightning-dir='${cfg.dataDir}' "$@" ${nbPkgs.clightning}/bin/lightning-cli --lightning-dir='${cfg.dataDir}' "$@"
''; '';
description = "Binary to connect with the clightning instance."; description = "Binary to connect with the clightning instance.";
@ -103,44 +100,34 @@ in {
}; };
environment.systemPackages = [ nbPkgs.clightning (hiPrio cfg.cli) ]; environment.systemPackages = [ nbPkgs.clightning (hiPrio cfg.cli) ];
users.users.${cfg.user} = {
description = "clightning User";
group = cfg.group;
extraGroups = [ "bitcoinrpc" ];
};
users.groups.${cfg.group} = {};
nix-bitcoin.operator.groups = [ cfg.group ];
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
]; ];
systemd.services.clightning = { systemd.services.clightning = {
description = "Run clightningd";
path = [ nbPkgs.bitcoind ]; path = [ nbPkgs.bitcoind ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
preStart = '' preStart = ''
cp ${configFile} ${cfg.dataDir}/config
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}' chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
# The RPC socket has to be removed otherwise we might have stale sockets # The RPC socket has to be removed otherwise we might have stale sockets
rm -f ${cfg.networkDir}/lightning-rpc rm -f ${cfg.networkDir}/lightning-rpc
chmod 640 ${cfg.dataDir}/config install -m 640 ${configFile} '${cfg.dataDir}/config'
{ {
echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-public)" echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-public)"
${optionalString (cfg.getPublicAddressCmd != "") '' ${optionalString (cfg.getPublicAddressCmd != "") ''
echo "announce-addr=$(${cfg.getPublicAddressCmd})" echo "announce-addr=$(${cfg.getPublicAddressCmd})"
''} ''}
} >> '${cfg.dataDir}/config' } >> '${cfg.dataDir}/config'
'';
'';
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
User = "${cfg.user}"; User = cfg.user;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nbLib.allowTor then nbLib.allowTor
else nbLib.allowAnyIP else nbLib.allowAnyIP
@ -154,5 +141,12 @@ in {
chmod g+x ${cfg.networkDir} chmod g+x ${cfg.networkDir}
''; '';
}; };
users.users.${cfg.user} = {
group = cfg.group;
extraGroups = [ "bitcoinrpc" ];
};
users.groups.${cfg.group} = {};
nix-bitcoin.operator.groups = [ cfg.group ];
}; };
} }

View File

@ -68,7 +68,6 @@ in {
]; ];
systemd.services.electrs = { systemd.services.electrs = {
description = "Electrs Electrum Server";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
@ -110,7 +109,6 @@ in {
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {
description = "electrs User";
group = cfg.group; group = cfg.group;
extraGroups = [ "bitcoinrpc" ] ++ optionals cfg.high-memory [ "bitcoin" ]; extraGroups = [ "bitcoinrpc" ] ++ optionals cfg.high-memory [ "bitcoin" ];
}; };

View File

@ -47,11 +47,12 @@ in {
# Provides lsusb for debugging # Provides lsusb for debugging
pkgs.usbutils pkgs.usbutils
]; ];
users.groups."${cfg.group}" = {};
users.groups.${cfg.group} = {};
nix-bitcoin.operator.groups = [ cfg.group ]; nix-bitcoin.operator.groups = [ cfg.group ];
}) })
(mkIf cfg.ledger {
(mkIf cfg.ledger {
# Ledger Nano S according to https://github.com/LedgerHQ/udev-rules/blob/master/add_udev_rules.sh # Ledger Nano S according to https://github.com/LedgerHQ/udev-rules/blob/master/add_udev_rules.sh
# Don't use rules from nixpkgs because we want to use our own group. # Don't use rules from nixpkgs because we want to use our own group.
services.udev.packages = lib.singleton (pkgs.writeTextFile { services.udev.packages = lib.singleton (pkgs.writeTextFile {

View File

@ -46,16 +46,6 @@ in {
default = "/var/lib/joinmarket-ob-watcher"; default = "/var/lib/joinmarket-ob-watcher";
description = "The data directory for JoinMarket orderbook watcher."; description = "The data directory for JoinMarket orderbook watcher.";
}; };
user = mkOption {
type = types.str;
default = "joinmarket-ob-watcher";
description = "The user as which to run JoinMarket orderbook watcher.";
};
group = mkOption {
type = types.str;
default = cfg.user;
description = "The group as which to run JoinMarket orderbook watcher.";
};
# This option is only used by netns-isolation # This option is only used by netns-isolation
enforceTor = mkOption { enforceTor = mkOption {
readOnly = true; readOnly = true;
@ -64,6 +54,7 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
# Joinmarket is Tor-only
services.tor = { services.tor = {
enable = true; enable = true;
client.enable = true; client.enable = true;
@ -73,27 +64,23 @@ in {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "tor.service" ]; requires = [ "tor.service" ];
after = [ "tor.service" ]; after = [ "tor.service" ];
# The service writes to HOME/.config/matplotlib
environment.HOME = cfg.dataDir;
preStart = '' preStart = ''
ln -snf ${configFile} ${cfg.dataDir}/joinmarket.cfg ln -snf ${configFile} ${cfg.dataDir}/joinmarket.cfg
''; '';
serviceConfig = nbLib.defaultHardening // rec { serviceConfig = nbLib.defaultHardening // rec {
DynamicUser = true;
StateDirectory = "joinmarket-ob-watcher"; StateDirectory = "joinmarket-ob-watcher";
StateDirectoryMode = "0770"; StateDirectoryMode = "0770";
WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir WorkingDirectory = cfg.dataDir; # The service creates dir 'logs' in the working dir
ExecStart = '' ExecStart = ''
${nbPkgs.joinmarket}/bin/ob-watcher --datadir=${cfg.dataDir} \ ${nbPkgs.joinmarket}/bin/ob-watcher --datadir=${cfg.dataDir} \
--host=${cfg.address} --port=${toString cfg.port} --host=${cfg.address} --port=${toString cfg.port}
''; '';
User = cfg.user;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nbLib.allowTor; } // nbLib.allowTor;
}; };
users.users.${cfg.user} = {
group = cfg.group;
home = cfg.dataDir; # The service writes to HOME/.config/matplotlib
};
users.groups.${cfg.group} = {};
}; };
} }

View File

@ -141,75 +141,79 @@ in {
}; };
config = mkIf cfg.enable (mkMerge [{ config = mkIf cfg.enable (mkMerge [{
services.bitcoind.enable = true; services.bitcoind = {
enable = true;
disablewallet = false;
};
# Joinmarket is Tor-only
services.tor = {
enable = true;
client.enable = true;
# Needed for payjoin onion service creation
controlSocket.enable = true;
};
environment.systemPackages = [ environment.systemPackages = [
(hiPrio cfg.cli) (hiPrio cfg.cli)
]; ];
users.users.${cfg.user} = {
description = "joinmarket User";
group = "${cfg.group}";
home = cfg.dataDir;
extraGroups = [ "tor" ];
};
users.groups.${cfg.group} = {};
nix-bitcoin.operator = {
groups = [ cfg.group ];
sudoUsers = [ cfg.group ];
};
systemd.tmpfiles.rules = [ systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
]; ];
services.bitcoind.disablewallet = false;
# Joinmarket is TOR-only
services.tor = {
enable = true;
client.enable = true;
controlSocket.enable = true;
};
systemd.services.joinmarket = { systemd.services.joinmarket = {
description = "JoinMarket Daemon";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
path = [ pkgs.sudo ]; path = [ pkgs.sudo ];
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStartPre = nbLib.privileged '' ExecStartPre = nbLib.privileged "joinmarket-create-config" ''
install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg
sed -i \ sed -i \
"s|@@RPC_PASSWORD@@|rpc_password = $(cat ${secretsDir}/bitcoin-rpcpassword-privileged)|" \ "s|@@RPC_PASSWORD@@|rpc_password = $(cat ${secretsDir}/bitcoin-rpcpassword-privileged)|" \
'${cfg.dataDir}/joinmarket.cfg' '${cfg.dataDir}/joinmarket.cfg'
''; '';
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet # Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
ExecStartPost = mkIf (bitcoind.network == "mainnet") (nbLib.privileged '' ExecStartPost = mkIf (bitcoind.network == "mainnet")
walletname=wallet.jmdat (nbLib.privileged "joinmarket-create-wallet" ''
pw=$(cat "${secretsDir}"/jm-wallet-password) walletname=wallet.jmdat
mnemonic=${secretsDir}/jm-wallet-seed wallet=${cfg.dataDir}/wallets/$walletname
if [[ ! -f ${cfg.dataDir}/wallets/$walletname ]]; then if [[ ! -f $wallet ]]; then
echo Create joinmarket wallet echo "Create wallet"
# Use bash variables so commands don't proceed on previous failures pw=$(cat "${secretsDir}"/jm-wallet-password)
# (like with pipes) cd ${cfg.dataDir}
cd ${cfg.dataDir} && \ if ! sudo -u ${cfg.user} ${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
out=$(sudo -u ${cfg.user} \ | grep 'recovery_seed' \
${nbPkgs.joinmarket}/bin/jm-genwallet \ | cut -d ':' -f2 \
--datadir=${cfg.dataDir} $walletname $pw) | (umask u=r,go=; cat > "${secretsDir}/jm-wallet-seed"); then
recoveryseed=$(echo "$out" | grep 'recovery_seed') echo "wallet creation failed"
echo "$recoveryseed" | cut -d ':' -f2 > $mnemonic rm -f "$wallet" "${secretsDir}/jm-wallet-seed"
fi exit 1
''); fi
fi
'');
ExecStart = "${nbPkgs.joinmarket}/bin/joinmarketd"; ExecStart = "${nbPkgs.joinmarket}/bin/joinmarketd";
WorkingDirectory = "${cfg.dataDir}"; # The service creates 'commitmentlist' in the working dir WorkingDirectory = cfg.dataDir; # The service creates 'commitmentlist' in the working dir
User = "${cfg.user}"; User = cfg.user;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
} // nbLib.allowTor; } // nbLib.allowTor;
}; };
users.users.${cfg.user} = {
group = cfg.group;
home = cfg.dataDir;
# Allow access to the tor control socket, needed for payjoin onion service creation
extraGroups = [ "tor" ];
};
users.groups.${cfg.group} = {};
nix-bitcoin.operator = {
groups = [ cfg.group ];
sudoUsers = [ cfg.group ];
};
nix-bitcoin.secrets.jm-wallet-password.user = cfg.user; nix-bitcoin.secrets.jm-wallet-password.user = cfg.user;
} }
@ -227,7 +231,6 @@ in {
chmod +x $out chmod +x $out
''; '';
in { in {
description = "CoinJoin maker bot to gain privacy and passively generate income";
wantedBy = [ "joinmarket.service" ]; wantedBy = [ "joinmarket.service" ];
requires = [ "joinmarket.service" ]; requires = [ "joinmarket.service" ];
after = [ "joinmarket.service" ]; after = [ "joinmarket.service" ];
@ -242,10 +245,14 @@ in {
serviceConfig = nbLib.defaultHardening // rec { serviceConfig = nbLib.defaultHardening // rec {
RuntimeDirectory = "joinmarket-yieldgenerator"; # Only used to create start script RuntimeDirectory = "joinmarket-yieldgenerator"; # Only used to create start script
RuntimeDirectoryMode = "700"; RuntimeDirectoryMode = "700";
WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir WorkingDirectory = cfg.dataDir; # The service creates dir 'logs' in the working dir
ExecStart = "${pkgs.bash}/bin/bash /run/${RuntimeDirectory}/start"; ExecStart = "${pkgs.bash}/bin/bash /run/${RuntimeDirectory}/start";
User = "${cfg.user}"; # Show "joinmarket-yieldgenerator" instead of "bash" in the journal.
ReadWritePaths = "${cfg.dataDir}"; # The parent bash start process has to run alongside the main process
# because it provides the wallet password via stdin to the main process
SyslogIdentifier = "joinmarket-yieldgenerator";
User = cfg.user;
ReadWritePaths = cfg.dataDir;
} // nbLib.allowTor; } // nbLib.allowTor;
}; };
}) })

View File

@ -101,7 +101,7 @@ in {
User = "lnd"; User = "lnd";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nbLib.allowTor then nbLib.allowTor
else nbLib.allowAnyIP); else nbLib.allowAnyIP);

View File

@ -87,9 +87,8 @@ in {
par=16 par=16
rpcthreads=16 rpcthreads=16
logips=1 logips=1
''; '';
description = "Additional configurations to be appended to <filename>elements.conf</filename>."; description = "Extra lines appended to <filename>elements.conf</filename>.";
}; };
dataDir = mkOption { dataDir = mkOption {
type = types.path; type = types.path;
@ -123,7 +122,7 @@ in {
alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae"; alice.passwordHMAC = "f7efda5c189b999524f151318c0c86$d5b51b3beffbc02b724e5d095828e0bc8b2456e9ac8757ae3211a5d9b16a22ae";
bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99"; bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
}; };
type = with types; loaOf (submodule rpcUserOpts); type = with types; attrsOf (submodule rpcUserOpts);
description = '' description = ''
RPC user information for JSON-RPC connections. RPC user information for JSON-RPC connections.
''; '';
@ -221,25 +220,25 @@ in {
]; ];
systemd.services.liquidd = { systemd.services.liquidd = {
description = "Elements daemon providing access to the Liquid sidechain";
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
preStart = '' preStart = ''
cp '${configFile}' '${cfg.dataDir}/elements.conf'
chmod 640 '${cfg.dataDir}/elements.conf'
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}' chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf' install -m 640 ${configFile} '${cfg.dataDir}/elements.conf'
echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword-public)" >> '${cfg.dataDir}/elements.conf' {
echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)"
echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword-public)"
} >> '${cfg.dataDir}/elements.conf'
''; '';
serviceConfig = nbLib.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
Type = "simple"; Type = "simple";
User = "${cfg.user}"; User = cfg.user;
Group = "${cfg.group}"; Group = cfg.group;
ExecStart = "${nbPkgs.elementsd}/bin/elementsd ${cmdlineOptions}"; ExecStart = "${nbPkgs.elementsd}/bin/elementsd ${cmdlineOptions}";
PIDFile = "${pidFile}"; PIDFile = pidFile;
Restart = "on-failure"; Restart = "on-failure";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nbLib.allowTor then nbLib.allowTor
else nbLib.allowAnyIP else nbLib.allowAnyIP
@ -249,7 +248,6 @@ in {
users.users.${cfg.user} = { users.users.${cfg.user} = {
group = cfg.group; group = cfg.group;
extraGroups = [ "bitcoinrpc" ]; extraGroups = [ "bitcoinrpc" ];
description = "Liquid sidechain user";
}; };
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = {};
nix-bitcoin.operator.groups = [ cfg.group ]; nix-bitcoin.operator.groups = [ cfg.group ];

View File

@ -37,13 +37,7 @@ let
in { in {
options.services.lnd = { options.services.lnd = {
enable = mkOption { enable = mkEnableOption "Lightning Network Daemon";
type = types.bool;
default = false;
description = ''
If enabled, the LND service will be installed.
'';
};
dataDir = mkOption { dataDir = mkOption {
type = types.path; type = types.path;
default = "/var/lib/lnd"; default = "/var/lib/lnd";
@ -89,7 +83,7 @@ in {
tor-socks = mkOption { tor-socks = mkOption {
type = types.nullOr types.str; type = types.nullOr types.str;
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null; default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
description = "Set a socks proxy to use to connect to Tor nodes"; description = "Socks proxy for connecting to Tor nodes";
}; };
macaroons = mkOption { macaroons = mkOption {
default = {}; default = {};
@ -118,7 +112,7 @@ in {
example = '' example = ''
autopilot.active=1 autopilot.active=1
''; '';
description = "Additional configurations to be appended to <filename>lnd.conf</filename>."; description = "Extra lines appended to <filename>lnd.conf</filename>.";
}; };
package = mkOption { package = mkOption {
type = types.package; type = types.package;
@ -156,9 +150,13 @@ in {
services.bitcoind = { services.bitcoind = {
enable = true; enable = true;
# Increase rpc thread count due to reports that lightning implementations fail # Increase rpc thread count due to reports that lightning implementations fail
# under high bitcoind rpc load # under high bitcoind rpc load
rpc.threads = 16; rpc.threads = 16;
zmqpubrawblock = "tcp://${bitcoindRpcAddress}:28332";
zmqpubrawtx = "tcp://${bitcoindRpcAddress}:28333";
}; };
environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ]; environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ];
@ -167,13 +165,7 @@ in {
"d '${cfg.dataDir}' 0770 lnd lnd - -" "d '${cfg.dataDir}' 0770 lnd lnd - -"
]; ];
services.bitcoind = {
zmqpubrawblock = "tcp://${bitcoindRpcAddress}:28332";
zmqpubrawtx = "tcp://${bitcoindRpcAddress}:28333";
};
systemd.services.lnd = { systemd.services.lnd = {
description = "Run LND";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
@ -193,12 +185,12 @@ in {
User = "lnd"; User = "lnd";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = cfg.dataDir;
ExecStartPost = let ExecStartPost = let
restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1"; restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1";
in [ in [
# Run fully privileged for secrets dir write access # Run fully privileged for secrets dir write access
"+${nbLib.script '' (nbLib.privileged "lnd-create-mnemonic" ''
attempts=250 attempts=250
while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; } ((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
@ -214,8 +206,8 @@ in {
-X GET ${restUrl}/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic" -X GET ${restUrl}/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic"
fi fi
chown lnd: "$mnemonic" chown lnd: "$mnemonic"
''}" '')
"${nbLib.script '' (nbLib.script "lnd-create-wallet" ''
if [[ ! -f ${networkDir}/wallet.db ]]; then if [[ ! -f ${networkDir}/wallet.db ]]; then
echo Create lnd wallet echo Create lnd wallet
@ -246,9 +238,9 @@ in {
sleep 0.1 sleep 0.1
done done
''}" '')
# Run fully privileged for chown # Run fully privileged for chown
"+${nbLib.script '' (nbLib.privileged "lnd-create-macaroons" ''
umask ug=r,o= umask ug=r,o=
${lib.concatMapStrings (macaroon: '' ${lib.concatMapStrings (macaroon: ''
echo "Create custom macaroon ${macaroon}" echo "Create custom macaroon ${macaroon}"
@ -262,7 +254,7 @@ in {
${pkgs.jq}/bin/jq -c '.macaroon' | ${pkgs.xxd}/bin/xxd -p -r > "$macaroonPath" ${pkgs.jq}/bin/jq -c '.macaroon' | ${pkgs.xxd}/bin/xxd -p -r > "$macaroonPath"
chown ${cfg.macaroons.${macaroon}.user}: "$macaroonPath" chown ${cfg.macaroons.${macaroon}.user}: "$macaroonPath"
'') (attrNames cfg.macaroons)} '') (attrNames cfg.macaroons)}
''}" '')
]; ];
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nbLib.allowTor then nbLib.allowTor
@ -271,7 +263,6 @@ in {
}; };
users.users.lnd = { users.users.lnd = {
description = "LND User";
group = "lnd"; group = "lnd";
extraGroups = [ "bitcoinrpc" ]; extraGroups = [ "bitcoinrpc" ];
home = cfg.dataDir; # lnd creates .lnd dir in HOME home = cfg.dataDir; # lnd creates .lnd dir in HOME

View File

@ -105,7 +105,7 @@ in {
source = config.nix-bitcoin.pkgs.netns-exec; source = config.nix-bitcoin.pkgs.netns-exec;
capabilities = "cap_sys_admin=ep"; capabilities = "cap_sys_admin=ep";
owner = cfg.allowedUser; owner = cfg.allowedUser;
permissions = "u+rx,g+rx,o-rwx"; permissions = "550";
}; };
systemd.services = { systemd.services = {
@ -119,7 +119,7 @@ in {
after = [ "network-pre.target" ]; after = [ "network-pre.target" ];
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = "yes"; RemainAfterExit = true;
}; };
script = '' script = ''
${ip} link add name nb-br type bridge ${ip} link add name nb-br type bridge
@ -182,7 +182,7 @@ in {
''; '';
serviceConfig = { serviceConfig = {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = "yes"; RemainAfterExit = true;
}; };
}; };
}; };

View File

@ -41,13 +41,7 @@ let
''; '';
in { in {
options.services.recurring-donations = { options.services.recurring-donations = {
enable = mkOption { enable = mkEnableOption "recurring-donations";
type = types.bool;
default = false;
description = ''
If enabled, the recurring-donations service will be installed.
'';
};
tallycoin = mkOption { tallycoin = mkOption {
type = types.attrs; type = types.attrs;
default = {}; default = {};
@ -81,15 +75,7 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
services.clightning.enable = true; services.clightning.enable = true;
users.users.recurring-donations = {
description = "recurring-donations User";
group = "recurring-donations";
extraGroups = [ "clightning" ];
};
users.groups.recurring-donations = {};
systemd.services.recurring-donations = { systemd.services.recurring-donations = {
description = "Run recurring-donations";
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];
after = [ "clightning.service" ]; after = [ "clightning.service" ];
path = with pkgs; [ nix-bitcoin.clightning curl sudo jq ]; path = with pkgs; [ nix-bitcoin.clightning curl sudo jq ];
@ -111,5 +97,11 @@ in {
}; };
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
}; };
users.users.recurring-donations = {
group = "recurring-donations";
extraGroups = [ "clightning" ];
};
users.groups.recurring-donations = {};
}; };
} }

View File

@ -23,13 +23,7 @@ let
''; '';
in { in {
options.services.spark-wallet = { options.services.spark-wallet = {
enable = mkOption { enable = mkEnableOption "spark-wallet";
type = types.bool;
default = false;
description = ''
If enabled, the spark-wallet service will be installed.
'';
};
address = mkOption { address = mkOption {
type = types.str; type = types.str;
default = "localhost"; default = "localhost";
@ -61,14 +55,12 @@ in {
services.clightning.enable = true; services.clightning.enable = true;
users.users.spark-wallet = { users.users.spark-wallet = {
description = "spark-wallet User";
group = "spark-wallet"; group = "spark-wallet";
extraGroups = [ "clightning" ]; extraGroups = [ "clightning" ];
}; };
users.groups.spark-wallet = {}; users.groups.spark-wallet = {};
systemd.services.spark-wallet = { systemd.services.spark-wallet = {
description = "Run spark-wallet";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];
after = [ "clightning.service" ]; after = [ "clightning.service" ];

View File

@ -3,20 +3,20 @@
, withGui }: , withGui }:
with stdenv.lib; with stdenv.lib;
stdenv.mkDerivation rec{ stdenv.mkDerivation rec {
name = "elements" + (toString (optional (!withGui) "d")) + "-" + version; pname = "elements${optionalString (!withGui) "d"}";
version = "0.18.1.9"; version = "0.18.1.9";
src = fetchurl { src = fetchurl {
urls = [ url = "https://github.com/ElementsProject/elements/archive/elements-${version}.tar.gz";
"https://github.com/ElementsProject/elements/archive/elements-${version}.tar.gz" # Use ./get-sha256.sh to fetch latest (verified) sha256
];
sha256 = "c6f1b040a896a1aaa7340f5cd48e119c84fef88df5d4c17d5ad5c13783f5b6c7"; sha256 = "c6f1b040a896a1aaa7340f5cd48e119c84fef88df5d4c17d5ad5c13783f5b6c7";
}; };
nativeBuildInputs = nativeBuildInputs =
[ pkgconfig autoreconfHook ] [ pkgconfig autoreconfHook ]
++ optional withGui wrapQtAppsHook; ++ optional withGui wrapQtAppsHook;
buildInputs = [ openssl db48 boost zlib zeromq buildInputs = [ openssl db48 boost zlib zeromq
miniupnpc protobuf libevent] miniupnpc protobuf libevent]
++ optionals stdenv.isLinux [ utillinux ] ++ optionals stdenv.isLinux [ utillinux ]
@ -27,10 +27,10 @@ stdenv.mkDerivation rec{
] ++ optionals (!doCheck) [ ] ++ optionals (!doCheck) [
"--disable-tests" "--disable-tests"
"--disable-gui-tests" "--disable-gui-tests"
] ] ++ optionals withGui [
++ optionals withGui [ "--with-gui=qt5" "--with-gui=qt5"
"--with-qt-bindir=${qtbase.dev}/bin:${qttools.dev}/bin" "--with-qt-bindir=${qtbase.dev}/bin:${qttools.dev}/bin"
]; ];
checkInputs = [ rapidcheck python3 ]; checkInputs = [ rapidcheck python3 ];

View File

@ -28,8 +28,9 @@ let self = {
CapabilityBoundingSet = ""; CapabilityBoundingSet = "";
# @system-service whitelist and docker seccomp blacklist (except for "clone" # @system-service whitelist and docker seccomp blacklist (except for "clone"
# which is a core requirement for systemd services) # which is a core requirement for systemd services)
# @system-service is defined in src/shared/seccomp-util.c (systemd source)
SystemCallFilter = [ "@system-service" "~add_key clone3 get_mempolicy kcmp keyctl mbind move_pages name_to_handle_at personality process_vm_readv process_vm_writev request_key set_mempolicy setns unshare userfaultfd" ]; SystemCallFilter = [ "@system-service" "~add_key clone3 get_mempolicy kcmp keyctl mbind move_pages name_to_handle_at personality process_vm_readv process_vm_writev request_key set_mempolicy setns unshare userfaultfd" ];
SystemCallArchitectures= "native"; SystemCallArchitectures = "native";
}; };
# nodejs applications apparently rely on memory write execute # nodejs applications apparently rely on memory write execute
@ -51,13 +52,13 @@ let self = {
''; '';
}; };
script = src: pkgs.writers.writeBash "script" '' script = name: src: pkgs.writers.writeBash name ''
set -eo pipefail set -eo pipefail
${src} ${src}
''; '';
# Used for ExecStart* # Used for ExecStart*
privileged = src: "+${self.script src}"; privileged = name: src: "+${self.script name src}";
cliExec = mkOption { cliExec = mkOption {
# Used by netns-isolation to execute the cli in the service's private netns # Used by netns-isolation to execute the cli in the service's private netns

View File

@ -6,7 +6,7 @@ buildPythonPackage rec {
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "freelan-developers"; owner = "freelan-developers";
repo = "chromalog"; repo = "chromalog";
rev = "${version}"; rev = version;
sha256 = "0pj4s52rgwlvwkzrj85y92c5r9c84pz8gga45jl5spysrv41y9p0"; sha256 = "0pj4s52rgwlvwkzrj85y92c5r9c84pz8gga45jl5spysrv41y9p0";
}; };

18
test/lib/copy-src.sh Normal file
View File

@ -0,0 +1,18 @@
# Re-run run-tests.sh in a snapshot copy of the source.
# Maintain /tmp/nix-bitcoin-src as a source cache to minimize copies.
tmp=$(mktemp -d '/tmp/nix-bitcoin-src.XXXXX')
# Ignore errors from now on
set +e
# Move source cache if it exists (atomic)
mv /tmp/nix-bitcoin-src $tmp/src 2>/dev/null
rsync -a --delete --exclude='.git*' "$scriptDir/../" $tmp/src && \
echo "Copied src" && \
_nixBitcoinInCopySrc=1 $tmp/src/test/run-tests.sh "${args[@]}"
# Set the current src as the source cache (atomic)
mv -T $tmp/src /tmp/nix-bitcoin-src 2>/dev/null
rm -rf $tmp

View File

@ -42,12 +42,20 @@
# For now, creating NixOS containers requires root permissions. # For now, creating NixOS containers requires root permissions.
# See ./lib/make-container.sh for a complete documentation. # See ./lib/make-container.sh for a complete documentation.
# #
# Run tests from a snapshot copy of the source files
# ./run-tests.sh --copy-src|-c ...
#
# This allows you to continue editing the nix-bitcoin sources while tests are running
# and reading source files.
# Files are copied to /tmp, a caching scheme helps minimizing copies.
#
# To add custom scenarios, set the environment variable `scenarioOverridesFile`. # To add custom scenarios, set the environment variable `scenarioOverridesFile`.
set -eo pipefail set -eo pipefail
scriptDir=$(cd "${BASH_SOURCE[0]%/*}" && pwd) scriptDir=$(cd "${BASH_SOURCE[0]%/*}" && pwd)
args=("$@")
scenario= scenario=
outLinkPrefix= outLinkPrefix=
ciBuild= ciBuild=
@ -77,6 +85,13 @@ while :; do
shift shift
ciBuild=1 ciBuild=1
;; ;;
--copy-src|-c)
shift
if [[ ! $_nixBitcoinInCopySrc ]]; then
. "$scriptDir/lib/copy-src.sh"
exit
fi
;;
*) *)
break break
esac esac
@ -172,7 +187,7 @@ doBuild() {
# Run the test by building the test derivation # Run the test by building the test derivation
buildTest() { buildTest() {
vmTestNixExpr | doBuild $scenario $outLinkArg "$@" - vmTestNixExpr | doBuild $scenario "$@" -
} }
vmTestNixExpr() { vmTestNixExpr() {