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
---
See the [examples directory](examples/README.md).
See [here for examples](examples/README.md).
Features
---
@ -76,7 +76,7 @@ NixOS modules
* Helper
* [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
* [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`)
Security

View File

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

View File

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

View File

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

View File

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

View File

@ -99,20 +99,34 @@ in {
services.clightning.enable = mkIf (cfg.btcpayserver.lightningBackend == "clightning") true;
services.lnd.enable = mkIf (cfg.btcpayserver.lightningBackend == "lnd") true;
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} - -"
services.bitcoind.rpc.users.btcpayserver = {
passwordHMACFromFile = true;
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 = {
enable = true;
ensureDatabases = [ "btcpaydb" ];
ensureUsers = [{
name = "${cfg.btcpayserver.user}";
name = cfg.btcpayserver.user;
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
configFile = builtins.toFile "config" ''
network=${config.services.bitcoind.network}
@ -123,12 +137,11 @@ in {
port=${toString cfg.nbxplorer.port}
'';
in {
description = "Run nbxplorer";
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
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)" \
>> '${cfg.nbxplorer.dataDir}/settings.config'
'';
@ -151,13 +164,13 @@ in {
systemd.services.btcpayserver = let
configFile = builtins.toFile "config" (''
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}
btcexplorerurl=http://${cfg.nbxplorer.address}:${toString cfg.nbxplorer.port}/
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}"}
port=${toString cfg.btcpayserver.port}
'' + optionalString (cfg.btcpayserver.lightningBackend == "clightning") ''
btclightning=type=clightning;server=unix:///${cfg.clightning.dataDir}/bitcoin/lightning-rpc
'');
@ -168,17 +181,17 @@ in {
"certthumbprint=";
in let self = {
wantedBy = [ "multi-user.target" ];
requires = [ "nbxplorer.service" ]
requires = [ "nbxplorer.service" "postgresql.service" ]
++ optional (cfg.btcpayserver.lightningBackend != null) "${cfg.btcpayserver.lightningBackend}.service";
after = self.requires;
preStart = ''
install -m 600 ${configFile} ${cfg.btcpayserver.dataDir}/settings.config
install -m 600 ${configFile} '${cfg.btcpayserver.dataDir}/settings.config'
${optionalString (cfg.btcpayserver.lightningBackend == "lnd") ''
{
echo -n "${lndConfig}";
${pkgs.openssl}/bin/openssl x509 -noout -fingerprint -sha256 -in ${config.nix-bitcoin.secretsDir}/lnd-cert \
| sed -e 's/.*=//;s/://g';
} >> ${cfg.btcpayserver.dataDir}/settings.config
} >> '${cfg.btcpayserver.dataDir}/settings.config'
''}
'';
serviceConfig = nbLib.defaultHardening // {
@ -197,20 +210,13 @@ in {
);
}; 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} = {
description = "nbxplorer user";
group = cfg.nbxplorer.group;
extraGroups = [ "bitcoinrpc" ];
home = cfg.nbxplorer.dataDir;
};
users.groups.${cfg.nbxplorer.group} = {};
users.users.${cfg.btcpayserver.user} = {
description = "btcpayserver user";
group = cfg.btcpayserver.group;
extraGroups = [ "nbxplorer" ]
++ optional (cfg.btcpayserver.lightningBackend == "clightning") cfg.clightning.user;
@ -218,18 +224,12 @@ in {
};
users.groups.${cfg.btcpayserver.group} = {};
services.bitcoind.rpc.users.btcpayserver = {
passwordHMACFromFile = true;
rpcwhitelist = cfg.bitcoind.rpc.users.public.rpcwhitelist ++ [
"setban"
"generatetoaddress"
"getpeerinfo"
];
};
nix-bitcoin.secrets.bitcoin-rpcpassword-btcpayserver = {
nix-bitcoin.secrets = {
bitcoin-rpcpassword-btcpayserver = {
user = "bitcoin";
group = "nbxplorer";
};
nix-bitcoin.secrets.bitcoin-HMAC-btcpayserver.user = "bitcoin";
bitcoin-HMAC-btcpayserver.user = "bitcoin";
};
};
}

View File

@ -11,7 +11,7 @@ let
network=${network}
bitcoin-datadir=${config.services.bitcoind.dataDir}
${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}
bitcoin-rpcconnect=${config.services.bitcoind.rpc.address}
bitcoin-rpcport=${toString config.services.bitcoind.rpc.port}
@ -21,13 +21,7 @@ let
'';
in {
options.services.clightning = {
enable = mkOption {
type = types.bool;
default = false;
description = ''
If enabled, the clightning service will be installed.
'';
};
enable = mkEnableOption "clightning";
address = mkOption {
type = types.str;
default = "127.0.0.1";
@ -41,13 +35,17 @@ in {
proxy = mkOption {
type = types.nullOr types.str;
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 {
type = types.bool;
default = cfg.enforceTor;
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 {
@ -63,7 +61,7 @@ in {
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Additional lines appended to the config file.";
description = "Extra lines appended to the configuration file.";
};
user = mkOption {
type = types.str;
@ -77,8 +75,7 @@ in {
};
cli = mkOption {
readOnly = true;
default = pkgs.writeScriptBin "lightning-cli"
''
default = pkgs.writeScriptBin "lightning-cli" ''
${nbPkgs.clightning}/bin/lightning-cli --lightning-dir='${cfg.dataDir}' "$@"
'';
description = "Binary to connect with the clightning instance.";
@ -103,44 +100,34 @@ in {
};
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 = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
systemd.services.clightning = {
description = "Run clightningd";
path = [ nbPkgs.bitcoind ];
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
preStart = ''
cp ${configFile} ${cfg.dataDir}/config
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
# The RPC socket has to be removed otherwise we might have stale sockets
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)"
${optionalString (cfg.getPublicAddressCmd != "") ''
echo "announce-addr=$(${cfg.getPublicAddressCmd})"
''}
} >> '${cfg.dataDir}/config'
'';
serviceConfig = nbLib.defaultHardening // {
ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
User = "${cfg.user}";
User = cfg.user;
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}";
ReadWritePaths = cfg.dataDir;
} // (if cfg.enforceTor
then nbLib.allowTor
else nbLib.allowAnyIP
@ -154,5 +141,12 @@ in {
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 = {
description = "Electrs Electrum Server";
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
@ -110,7 +109,6 @@ in {
};
users.users.${cfg.user} = {
description = "electrs User";
group = cfg.group;
extraGroups = [ "bitcoinrpc" ] ++ optionals cfg.high-memory [ "bitcoin" ];
};

View File

@ -47,11 +47,12 @@ in {
# Provides lsusb for debugging
pkgs.usbutils
];
users.groups."${cfg.group}" = {};
users.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
# Don't use rules from nixpkgs because we want to use our own group.
services.udev.packages = lib.singleton (pkgs.writeTextFile {

View File

@ -46,16 +46,6 @@ in {
default = "/var/lib/joinmarket-ob-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
enforceTor = mkOption {
readOnly = true;
@ -64,6 +54,7 @@ in {
};
config = mkIf cfg.enable {
# Joinmarket is Tor-only
services.tor = {
enable = true;
client.enable = true;
@ -73,27 +64,23 @@ in {
wantedBy = [ "multi-user.target" ];
requires = [ "tor.service" ];
after = [ "tor.service" ];
# The service writes to HOME/.config/matplotlib
environment.HOME = cfg.dataDir;
preStart = ''
ln -snf ${configFile} ${cfg.dataDir}/joinmarket.cfg
'';
serviceConfig = nbLib.defaultHardening // rec {
DynamicUser = true;
StateDirectory = "joinmarket-ob-watcher";
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 = ''
${nbPkgs.joinmarket}/bin/ob-watcher --datadir=${cfg.dataDir} \
--host=${cfg.address} --port=${toString cfg.port}
'';
User = cfg.user;
Restart = "on-failure";
RestartSec = "10s";
} // 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 [{
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 = [
(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 = [
"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 = {
description = "JoinMarket Daemon";
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
path = [ pkgs.sudo ];
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
sed -i \
"s|@@RPC_PASSWORD@@|rpc_password = $(cat ${secretsDir}/bitcoin-rpcpassword-privileged)|" \
'${cfg.dataDir}/joinmarket.cfg'
'';
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
ExecStartPost = mkIf (bitcoind.network == "mainnet") (nbLib.privileged ''
ExecStartPost = mkIf (bitcoind.network == "mainnet")
(nbLib.privileged "joinmarket-create-wallet" ''
walletname=wallet.jmdat
wallet=${cfg.dataDir}/wallets/$walletname
if [[ ! -f $wallet ]]; then
echo "Create wallet"
pw=$(cat "${secretsDir}"/jm-wallet-password)
mnemonic=${secretsDir}/jm-wallet-seed
if [[ ! -f ${cfg.dataDir}/wallets/$walletname ]]; then
echo Create joinmarket wallet
# Use bash variables so commands don't proceed on previous failures
# (like with pipes)
cd ${cfg.dataDir} && \
out=$(sudo -u ${cfg.user} \
${nbPkgs.joinmarket}/bin/jm-genwallet \
--datadir=${cfg.dataDir} $walletname $pw)
recoveryseed=$(echo "$out" | grep 'recovery_seed')
echo "$recoveryseed" | cut -d ':' -f2 > $mnemonic
cd ${cfg.dataDir}
if ! sudo -u ${cfg.user} ${nbPkgs.joinmarket}/bin/jm-genwallet --datadir=${cfg.dataDir} $walletname $pw \
| grep 'recovery_seed' \
| cut -d ':' -f2 \
| (umask u=r,go=; cat > "${secretsDir}/jm-wallet-seed"); then
echo "wallet creation failed"
rm -f "$wallet" "${secretsDir}/jm-wallet-seed"
exit 1
fi
fi
'');
ExecStart = "${nbPkgs.joinmarket}/bin/joinmarketd";
WorkingDirectory = "${cfg.dataDir}"; # The service creates 'commitmentlist' in the working dir
User = "${cfg.user}";
WorkingDirectory = cfg.dataDir; # The service creates 'commitmentlist' in the working dir
User = cfg.user;
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}";
ReadWritePaths = cfg.dataDir;
} // 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;
}
@ -227,7 +231,6 @@ in {
chmod +x $out
'';
in {
description = "CoinJoin maker bot to gain privacy and passively generate income";
wantedBy = [ "joinmarket.service" ];
requires = [ "joinmarket.service" ];
after = [ "joinmarket.service" ];
@ -242,10 +245,14 @@ in {
serviceConfig = nbLib.defaultHardening // rec {
RuntimeDirectory = "joinmarket-yieldgenerator"; # Only used to create start script
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";
User = "${cfg.user}";
ReadWritePaths = "${cfg.dataDir}";
# Show "joinmarket-yieldgenerator" instead of "bash" in the journal.
# 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;
};
})

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -28,8 +28,9 @@ let self = {
CapabilityBoundingSet = "";
# @system-service whitelist and docker seccomp blacklist (except for "clone"
# 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" ];
SystemCallArchitectures= "native";
SystemCallArchitectures = "native";
};
# 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
${src}
'';
# Used for ExecStart*
privileged = src: "+${self.script src}";
privileged = name: src: "+${self.script name src}";
cliExec = mkOption {
# Used by netns-isolation to execute the cli in the service's private netns

View File

@ -6,7 +6,7 @@ buildPythonPackage rec {
src = fetchFromGitHub {
owner = "freelan-developers";
repo = "chromalog";
rev = "${version}";
rev = version;
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.
# 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`.
set -eo pipefail
scriptDir=$(cd "${BASH_SOURCE[0]%/*}" && pwd)
args=("$@")
scenario=
outLinkPrefix=
ciBuild=
@ -77,6 +85,13 @@ while :; do
shift
ciBuild=1
;;
--copy-src|-c)
shift
if [[ ! $_nixBitcoinInCopySrc ]]; then
. "$scriptDir/lib/copy-src.sh"
exit
fi
;;
*)
break
esac
@ -172,7 +187,7 @@ doBuild() {
# Run the test by building the test derivation
buildTest() {
vmTestNixExpr | doBuild $scenario $outLinkArg "$@" -
vmTestNixExpr | doBuild $scenario "$@" -
}
vmTestNixExpr() {