diff --git a/README.md b/README.md index e18f8e9..1402e8d 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Features A [configuration preset](modules/presets/secure-node.nix) for setting up a secure node * All applications use Tor for outbound connections and support accepting inbound connections via onion services. -NixOS modules +NixOS modules ([src](modules/modules.nix)) * Application services * [bitcoind](https://github.com/bitcoin/bitcoin), with a default banlist against spy nodes * [clightning](https://github.com/ElementsProject/lightning) with support for announcing an onion service\ @@ -85,19 +85,19 @@ NixOS modules Security --- -* **Simplicity:** Only services you select in `configuration.nix` and their dependencies are installed, packages and dependencies are [pinned](pkgs/nixpkgs-pinned.nix), support for [doas](https://github.com/Duncaen/OpenDoas) ([sudo alternative](https://lobste.rs/s/efsvqu/heap_based_buffer_overflow_sudo_cve_2021#c_c6fcfa)), most packages are built from the [NixOS stable channel](https://github.com/NixOS/nixpkgs/tree/nixos-20.09), with a few exceptions that are built from the nixpkgs unstable channel, builds happen in a [sandboxed environment](https://nixos.org/manual/nix/stable/#conf-sandbox), code is continuously reviewed and refined. -* **Integrity:** Nix package manager, NixOS and packages can be built from source to reduce reliance on binary caches, nix-bitcoin merge commits are signed, all commits are approved by multiple nix-bitcoin developers, upstream packages are cryptographically verified where possible, we use this software ourselves. -* **Principle of Least Privilege:** Services operate with least privileges; they each have their own user and are restricted further with [systemd options](pkgs/lib.nix), [RPC whitelisting](modules/bitcoind-rpc-public-whitelist.nix), and [netns-isolation](modules/netns-isolation.nix). There's a non-root user *operator* to interact with the various services. -* **Defense-in-depth:** nix-bitcoin is built with a [hardened kernel](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix) by default, services are confined through discretionary access control, Linux namespaces, [dbus firewall](modules/security.nix) and seccomp-bpf with continuous improvements. +* **Simplicity:** Only services enabled in `configuration.nix` and their dependencies are installed, support for [doas](https://github.com/Duncaen/OpenDoas) ([sudo alternative](https://lobste.rs/s/efsvqu/heap_based_buffer_overflow_sudo_cve_2021#c_c6fcfa)), code is continuously reviewed and refined. +* **Integrity:** The Nix package manager guarantees that all dependencies are exactly specified, packages can be built from source to reduce reliance on binary caches, nix-bitcoin merge commits are signed, all commits are approved by multiple nix-bitcoin developers, upstream packages are cryptographically verified where possible, we use this software ourselves. +* **Principle of Least Privilege:** Services operate with least privileges; they each have their own user and are restricted further with [systemd features](pkgs/lib.nix), [RPC whitelisting](modules/bitcoind-rpc-public-whitelist.nix) and [netns-isolation](modules/netns-isolation.nix). There's a non-root user *operator* to interact with the various services. +* **Defense-in-depth:** nix-bitcoin supports a [hardened kernel](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix), services are confined through discretionary access control, Linux namespaces, [dbus firewall](modules/security.nix) and seccomp-bpf with continuous improvements. Note that if the machine you're deploying *from* is insecure, there is nothing nix-bitcoin can do to protect itself. Docs --- -* [FAQ](docs/faq.md) * [Hardware Requirements](docs/hardware.md) * [Install instructions](docs/install.md) * [Usage instructions](docs/usage.md) +* [FAQ](docs/faq.md) Troubleshooting --- diff --git a/docs/hardware.md b/docs/hardware.md index 46279e8..86a4362 100644 --- a/docs/hardware.md +++ b/docs/hardware.md @@ -1,8 +1,18 @@ Hardware requirements --- -* Disk space: 500 GB (400GB for Bitcoin blockchain + some room) - * Bitcoin Core pruning is not supported at the moment because it's not supported by c-lightning. It's possible to use pruning but you need to know what you're doing. -* RAM: 2GB of memory. ECC memory is better. Additionally, it's recommended to use DDR4 memory with targeted row refresh (TRR) enabled (https://rambleed.com/). +* RAM: 2GB. ECC memory is better. Additionally, it's recommended to use DDR4 memory with + targeted row refresh (TRR) enabled (https://rambleed.com/). +* Disk space: 500 GB (400GB for Bitcoin blockchain + some room) for an unpruned + instance of Bitcoin Core. + * This can be significantly lowered by enabling pruning. + Note: Pruning is not supported by `electrs`. -Tested hardware includes [pcengine's apu2c4](https://pcengines.ch/apu2c4.htm), [GB-BACE-3150](https://www.gigabyte.com/Mini-PcBarebone/GB-BACE-3150-rev-10), [GB-BACE-3160](https://www.gigabyte.com/de/Mini-PcBarebone/GB-BACE-3160-rev-10#ov). -Some hardware (including Intel NUCs) may not be compatible with the hardened kernel turned on by default (see https://github.com/fort-nix/nix-bitcoin/issues/39#issuecomment-517366093 for a workaround). +Tested low-end hardware includes: +- [Raspberry Pi 4](https://www.raspberrypi.org/products/raspberry-pi-4-model-b/) +- [PC Engines apu2c4](https://pcengines.ch/apu2c4.htm) +- [Gigabyte GB-BACE-3150](https://www.gigabyte.com/Mini-PcBarebone/GB-BACE-3150-rev-10) +- [Gigabyte GB-BACE-3160](https://www.gigabyte.com/de/Mini-PcBarebone/GB-BACE-3160-rev-10#ov) + +Some hardware (including Intel NUCs) may not be compatible with the [hardened kernel preset](../modules/presets/hardened.nix) +(See https://github.com/fort-nix/nix-bitcoin/issues/39#issuecomment-517366093 +for a workaround). diff --git a/examples/configuration.nix b/examples/configuration.nix index dcf1f06..26f3cfc 100644 --- a/examples/configuration.nix +++ b/examples/configuration.nix @@ -243,6 +243,10 @@ openssh.authorizedKeys.keys = [ "" ]; }; + # FIXME: Uncomment this to allow the operator user to run + # commands as root with `sudo` or `doas` + # users.users.operator.extraGroups = [ "wheel" ]; + # FIXME: add packages you need in your system environment.systemPackages = with pkgs; [ vim @@ -261,5 +265,4 @@ # When upgrading to a backwards-incompatible release, nix-bitcoin will display an # an error and provide hints for migrating your config to the new release. nix-bitcoin.configVersion = "0.0.51"; - } diff --git a/modules/backups.nix b/modules/backups.nix index 8cda1a6..8b140cf 100644 --- a/modules/backups.nix +++ b/modules/backups.nix @@ -40,14 +40,25 @@ let cfg = config.services.backups; - 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"} + # Potential backup file paths are are matched against filelist + # entries from top to bottom. + # The first match determines inclusion or exclusion. + filelist = builtins.toFile "filelist.txt" '' + ${builtins.concatStringsSep "\n" cfg.extraFiles} + + ${optionalString (!cfg.with-bulk-data) '' + - ${config.services.bitcoind.dataDir}/blocks + - ${config.services.bitcoind.dataDir}/chainstate + - ${config.services.bitcoind.dataDir}/indexes + ''} ${config.services.bitcoind.dataDir} ${config.services.clightning.dataDir} ${config.services.lnd.dataDir} - ${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}/*/blocks + - ${config.services.liquidd.dataDir}/*/chainstate + - ${config.services.liquidd.dataDir}/*/indexes + ''} ${config.services.liquidd.dataDir} ${optionalString cfg.with-bulk-data "${config.services.electrs.dataDir}"} ${config.services.nbxplorer.dataDir} @@ -59,9 +70,6 @@ let ${builtins.concatStringsSep "\n" postgresqlBackupPaths} - # Extra files - ${builtins.concatStringsSep "\n" cfg.extraFiles} - # Exclude all unspecified files and directories - / ''; diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index e52b525..07da64d 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -26,7 +26,6 @@ let package = mkOption { type = types.package; default = config.nix-bitcoin.pkgs.bitcoind; - defaultText = "pkgs.blockchains.bitcoind"; description = "The package providing bitcoin binaries."; }; extraConfig = mkOption { diff --git a/modules/btcpayserver.nix b/modules/btcpayserver.nix index aa2422f..cc959aa 100644 --- a/modules/btcpayserver.nix +++ b/modules/btcpayserver.nix @@ -101,23 +101,36 @@ let nbLib = config.nix-bitcoin.lib; nbPkgs = config.nix-bitcoin.pkgs; - bitcoind = config.services.bitcoind; + inherit (config.services) bitcoind liquidd; in { inherit options; config = mkIf cfg.btcpayserver.enable { - services.bitcoind.enable = true; + services.bitcoind = { + enable = true; + rpc.users.btcpayserver = { + passwordHMACFromFile = true; + rpcwhitelist = cfg.bitcoind.rpc.users.public.rpcwhitelist ++ [ + "setban" + "generatetoaddress" + "getpeerinfo" + ]; + }; + # Enable p2p connections + listen = true; + extraConfig = '' + whitelist=${nbLib.address cfg.nbxplorer.address} + ''; + }; services.clightning.enable = mkIf (cfg.btcpayserver.lightningBackend == "clightning") true; services.lnd.enable = mkIf (cfg.btcpayserver.lightningBackend == "lnd") true; - services.liquidd.enable = mkIf cfg.btcpayserver.lbtc true; - - services.bitcoind.rpc.users.btcpayserver = { - passwordHMACFromFile = true; - rpcwhitelist = cfg.bitcoind.rpc.users.public.rpcwhitelist ++ [ - "setban" - "generatetoaddress" - "getpeerinfo" - ]; + services.liquidd = mkIf cfg.btcpayserver.lbtc { + enable = true; + # Enable p2p connections + listen = true; + extraConfig = '' + whitelist=${nbLib.address cfg.nbxplorer.address} + ''; }; services.lnd.macaroons.btcpayserver = mkIf (cfg.btcpayserver.lightningBackend == "lnd") { @@ -143,15 +156,15 @@ in { configFile = builtins.toFile "config" '' network=${bitcoind.network} btcrpcuser=${cfg.bitcoind.rpc.users.btcpayserver.name} - btcrpcurl=http://${bitcoind.rpc.address}:${toString cfg.bitcoind.rpc.port} - btcnodeendpoint=${bitcoind.address}:${toString bitcoind.port} + btcrpcurl=http://${nbLib.addressWithPort bitcoind.rpc.address cfg.bitcoind.rpc.port} + btcnodeendpoint=${nbLib.addressWithPort bitcoind.address bitcoind.port} bind=${cfg.nbxplorer.address} port=${toString cfg.nbxplorer.port} ${optionalString cfg.btcpayserver.lbtc '' chains=btc,lbtc - lbtcrpcuser=${cfg.liquidd.rpcuser} - lbtcrpcurl=http://${cfg.liquidd.rpc.address}:${toString cfg.liquidd.rpc.port} - lbtcnodeendpoint=${cfg.liquidd.address}:${toString cfg.liquidd.port} + lbtcrpcuser=${liquidd.rpcuser} + lbtcrpcurl=http://${nbLib.addressWithPort liquidd.rpc.address liquidd.rpc.port} + lbtcnodeendpoint=${nbLib.addressWithPort liquidd.address liquidd.port} ''} ''; in { @@ -221,8 +234,8 @@ in { ''; serviceConfig = nbLib.defaultHardening // { ExecStart = '' - ${cfg.btcpayserver.package}/bin/btcpayserver --conf=${cfg.btcpayserver.dataDir}/settings.config \ - --datadir=${cfg.btcpayserver.dataDir} + ${cfg.btcpayserver.package}/bin/btcpayserver --conf='${cfg.btcpayserver.dataDir}/settings.config' \ + --datadir='${cfg.btcpayserver.dataDir}' ''; User = cfg.btcpayserver.user; Restart = "on-failure"; @@ -236,7 +249,7 @@ in { isSystemUser = true; group = cfg.nbxplorer.group; extraGroups = [ "bitcoinrpc-public" ] - ++ optional cfg.btcpayserver.lbtc cfg.liquidd.group; + ++ optional cfg.btcpayserver.lbtc liquidd.group; home = cfg.nbxplorer.dataDir; }; users.groups.${cfg.nbxplorer.group} = {}; diff --git a/modules/charge-lnd.nix b/modules/charge-lnd.nix index f6579c1..6f564dc 100644 --- a/modules/charge-lnd.nix +++ b/modules/charge-lnd.nix @@ -112,9 +112,10 @@ in ExecStart = '' ${config.nix-bitcoin.pkgs.charge-lnd}/bin/charge-lnd \ --lnddir ${dataDir}/lnddir-proxy \ - --grpc ${lnd.rpcAddress}:${toString lnd.rpcPort} \ + --grpc ${nbLib.addressWithPort lnd.rpcAddress lnd.rpcPort} \ --config ${checkedConfig} \ - ${optionalString (electrs != null) "--electrum-server ${electrs.address}:${toString electrs.port}"} \ + ${optionalString (electrs != null) + "--electrum-server ${nbLib.addressWithPort electrs.address electrs.port}"} \ ${escapeShellArgs cfg.extraFlags} ''; Type = "oneshot"; diff --git a/modules/clightning.nix b/modules/clightning.nix index 58a59bd..19d8b31 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -7,7 +7,7 @@ let address = mkOption { type = types.str; default = "127.0.0.1"; - description = "IP address or UNIX domain socket to listen for peer connections."; + description = "Address to listen for peer connections."; }; port = mkOption { type = types.port; @@ -84,7 +84,7 @@ let ${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"} always-use-proxy=${boolToString cfg.always-use-proxy} bind-addr=${cfg.address}:${toString cfg.port} - bitcoin-rpcconnect=${config.services.bitcoind.rpc.address} + bitcoin-rpcconnect=${nbLib.address config.services.bitcoind.rpc.address} bitcoin-rpcport=${toString config.services.bitcoind.rpc.port} bitcoin-rpcuser=${config.services.bitcoind.rpc.users.public.name} rpc-file-mode=0660 diff --git a/modules/electrs.nix b/modules/electrs.nix index 095f6a0..15cad4c 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -96,7 +96,7 @@ in { --daemon-dir='${bitcoind.dataDir}' \ --electrum-rpc-addr=${cfg.address}:${toString cfg.port} \ --monitoring-addr=${cfg.address}:${toString cfg.monitoringPort} \ - --daemon-rpc-addr=${bitcoind.rpc.address}:${toString bitcoind.rpc.port} \ + --daemon-rpc-addr=${nbLib.addressWithPort bitcoind.rpc.address bitcoind.rpc.port} \ ${cfg.extraArgs} ''; User = cfg.user; diff --git a/modules/joinmarket-ob-watcher.nix b/modules/joinmarket-ob-watcher.nix index b5378b5..faa485c 100644 --- a/modules/joinmarket-ob-watcher.nix +++ b/modules/joinmarket-ob-watcher.nix @@ -93,10 +93,10 @@ in { "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" ]; - systemd.services.joinmarket-ob-watcher = { + systemd.services.joinmarket-ob-watcher = rec { wantedBy = [ "multi-user.target" ]; - requires = [ "tor.service" ]; - after = [ "tor.service" ]; + requires = [ "tor.service" "bitcoind.service" ]; + after = requires; # The service writes to HOME/.config/matplotlib environment.HOME = cfg.dataDir; preStart = '' diff --git a/modules/joinmarket.nix b/modules/joinmarket.nix index f2e92d9..9ac4d8a 100644 --- a/modules/joinmarket.nix +++ b/modules/joinmarket.nix @@ -124,7 +124,7 @@ let [BLOCKCHAIN] blockchain_source = bitcoin-rpc network = ${bitcoind.network} - rpc_host = ${bitcoind.rpc.address} + rpc_host = ${nbLib.address bitcoind.rpc.address} rpc_port = ${toString bitcoind.rpc.port} rpc_user = ${bitcoind.rpc.users.privileged.name} ${optionalString (cfg.rpcWalletFile != null) "rpc_wallet_file = ${cfg.rpcWalletFile}"} diff --git a/modules/lightning-loop.nix b/modules/lightning-loop.nix index b7b388e..1249a3a 100644 --- a/modules/lightning-loop.nix +++ b/modules/lightning-loop.nix @@ -50,7 +50,7 @@ let cli = mkOption { default = pkgs.writeScriptBin "loop" '' ${cfg.package}/bin/loop \ - --rpcserver ${rpclisten} \ + --rpcserver ${nbLib.addressWithPort cfg.rpcAddress cfg.rpcPort} \ --macaroonpath '${cfg.dataDir}/${network}/loop.macaroon' \ --tlscertpath '${secretsDir}/loop-cert' "$@" ''; @@ -66,17 +66,16 @@ let lnd = config.services.lnd; network = config.services.bitcoind.network; - rpclisten = "${cfg.rpcAddress}:${toString cfg.rpcPort}"; configFile = builtins.toFile "loop.conf" '' datadir=${cfg.dataDir} network=${network} - rpclisten=${rpclisten} + rpclisten=${cfg.rpcAddress}:${toString cfg.rpcPort} restlisten=${cfg.restAddress}:${toString cfg.restPort} logdir=${cfg.dataDir}/logs tlscertpath=${secretsDir}/loop-cert tlskeypath=${secretsDir}/loop-key - lnd.host=${lnd.rpcAddress}:${toString lnd.rpcPort} + lnd.host=${nbLib.addressWithPort lnd.rpcAddress lnd.rpcPort} lnd.macaroonpath=${lnd.networkDir}/admin.macaroon lnd.tlspath=${lnd.certPath} diff --git a/modules/lightning-pool.nix b/modules/lightning-pool.nix index 4a9c9ca..0c69d32 100644 --- a/modules/lightning-pool.nix +++ b/modules/lightning-pool.nix @@ -50,7 +50,7 @@ let cli = mkOption { default = pkgs.writeScriptBin "pool" '' exec ${cfg.package}/bin/pool \ - --rpcserver ${rpclisten} \ + --rpcserver ${nbLib.addressWithPort cfg.rpcAddress cfg.rpcPort} \ --network ${network} \ --basedir '${cfg.dataDir}' "$@" ''; @@ -65,9 +65,8 @@ let lnd = config.services.lnd; network = config.services.bitcoind.network; - rpclisten = "${cfg.rpcAddress}:${toString cfg.rpcPort}"; configFile = builtins.toFile "pool.conf" '' - rpclisten=${rpclisten} + rpclisten=${cfg.rpcAddress}:${toString cfg.rpcPort} restlisten=${cfg.restAddress}:${toString cfg.restPort} ${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"} diff --git a/modules/liquid.nix b/modules/liquid.nix index 9a311fc..a0714e9 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -143,7 +143,6 @@ let bitcoind = config.services.bitcoind; - pidFile = "${cfg.dataDir}/liquidd.pid"; configFile = pkgs.writeText "elements.conf" '' chain=${bitcoind.makeNetworkName "liquidv1" '' regtest @@ -169,18 +168,13 @@ let rpcconnect=${cfg.rpc.address} ${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip} rpcuser=${cfg.rpcuser} - mainchainrpchost=${bitcoind.rpc.address} + mainchainrpchost=${nbLib.address bitcoind.rpc.address} mainchainrpcport=${toString bitcoind.rpc.port} mainchainrpcuser=${bitcoind.rpc.users.public.name} # Extra config options (from liquidd nixos service) ${cfg.extraConfig} ''; - cmdlineOptions = concatMapStringsSep " " (arg: "'${arg}'") [ - "-datadir=${cfg.dataDir}" - "-pid=${pidFile}" - ]; - hexStr = types.strMatching "[0-9a-f]+"; rpcUserOpts = { name, ... }: { options = { name = mkOption { @@ -234,8 +228,7 @@ in { Type = "simple"; User = cfg.user; Group = cfg.group; - ExecStart = "${nbPkgs.elementsd}/bin/elementsd ${cmdlineOptions}"; - PIDFile = pidFile; + ExecStart = "${nbPkgs.elementsd}/bin/elementsd -datadir='${cfg.dataDir}'"; Restart = "on-failure"; ReadWritePaths = cfg.dataDir; } // nbLib.allowedIPAddresses cfg.enforceTor; diff --git a/modules/lnd-rest-onion-service.nix b/modules/lnd-rest-onion-service.nix index 8b2ea25..3d845c1 100644 --- a/modules/lnd-rest-onion-service.nix +++ b/modules/lnd-rest-onion-service.nix @@ -41,7 +41,7 @@ in { services.tor = { enable = true; relay.onionServices.lnd-rest = nbLib.mkOnionService { - target.addr = lnd.restAddress; + target.addr = nbLib.address lnd.restAddress; target.port = lnd.restPort; port = lnd.restPort; }; diff --git a/modules/lnd.nix b/modules/lnd.nix index 648a2c1..6da0f18 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -127,7 +127,7 @@ let bitcoind = config.services.bitcoind; - bitcoindRpcAddress = bitcoind.rpc.address; + bitcoindRpcAddress = nbLib.address bitcoind.rpc.address; networkDir = cfg.networkDir; configFile = pkgs.writeText "lnd.conf" '' datadir=${cfg.dataDir} @@ -217,11 +217,13 @@ in { # existing, but the RPC service isn't yet, which results in error # "waiting to start, RPC services not available". curl = "${pkgs.curl}/bin/curl -s --show-error --retry 10 --cacert ${cfg.certPath}"; - restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1"; + restUrl = "https://${nbLib.addressWithPort cfg.restAddress cfg.restPort}/v1"; in [ (nbLib.script "lnd-create-wallet" '' attempts=250 - while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do + while ! { + exec 3>/dev/tcp/${nbLib.address cfg.restAddress}/${toString cfg.restPort} && exec 3>&- + } &>/dev/null; do ((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; } sleep 0.1 done diff --git a/modules/modules.nix b/modules/modules.nix index 0e0ac5a..97c263a 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -18,9 +18,9 @@ ./lightning-loop.nix ./lightning-pool.nix ./charge-lnd.nix - ./btcpayserver.nix ./electrs.nix ./liquid.nix + ./btcpayserver.nix ./joinmarket.nix ./joinmarket-ob-watcher.nix ./hardware-wallets.nix diff --git a/modules/netns-isolation.nix b/modules/netns-isolation.nix index 88e486d..d0dca07 100644 --- a/modules/netns-isolation.nix +++ b/modules/netns-isolation.nix @@ -47,6 +47,12 @@ let readOnly = true; description = "Exposes netns parameters."; }; + + bridgeIp = mkOption { + default = bridgeIp; + readOnly = true; + description = "IP of the netns bridge interface."; + }; }; cfg = config.nix-bitcoin.netns-isolation; diff --git a/modules/nix-bitcoin.nix b/modules/nix-bitcoin.nix index 48cb7c4..bad87c1 100644 --- a/modules/nix-bitcoin.nix +++ b/modules/nix-bitcoin.nix @@ -16,7 +16,7 @@ with lib; torClientAddressWithPort = mkOption { readOnly = true; - default = with config.services.tor.client.socksListenAddress; + default = with config.services.tor.client.socksListenAddress; "${addr}:${toString port}"; }; diff --git a/modules/nodeinfo.nix b/modules/nodeinfo.nix index 5a9599c..8bba8e0 100644 --- a/modules/nodeinfo.nix +++ b/modules/nodeinfo.nix @@ -13,6 +13,7 @@ let }; cfg = config.nix-bitcoin.nodeinfo; + nbLib = config.nix-bitcoin.lib; # Services included in the output services = { @@ -96,7 +97,7 @@ let mkInfo = extraCode: name: cfg: '' add_service("${name}", """ - info["local_address"] = "${cfg.address}:${toString cfg.port}" + info["local_address"] = "${nbLib.addressWithPort cfg.address cfg.port}" '' + mkIfOnionPort name (onionPort: '' set_onion_address(info, "${name}", ${onionPort}) '') + extraCode + '' diff --git a/modules/onion-services.nix b/modules/onion-services.nix index 25c9f45..490f136 100644 --- a/modules/onion-services.nix +++ b/modules/onion-services.nix @@ -43,18 +43,18 @@ let cfg = config.nix-bitcoin.onionServices; nbLib = config.nix-bitcoin.lib; - services = builtins.attrNames cfg; + onionServices = builtins.attrNames cfg; activeServices = builtins.filter (service: config.services.${service}.enable && cfg.${service}.enable - ) services; + ) onionServices; publicServices = builtins.filter (service: cfg.${service}.public) activeServices; in { inherit options; config = mkMerge [ - (mkIf (cfg != {}) { + (mkIf (activeServices != []) { # Define hidden services services.tor = { enable = true; @@ -65,7 +65,7 @@ in { in nbLib.mkOnionService { port = if externalPort != null then externalPort else service.port; target.port = service.port; - target.addr = if service.address == "0.0.0.0" then "127.0.0.1" else service.address; + target.addr = nbLib.address service.address; } ); }; @@ -93,7 +93,7 @@ in { publicServices' = builtins.filter (service: let srv = cfg.${service}; in srv.public && srv.enable - ) services; + ) onionServices; in genAttrs publicServices' (service: { getPublicAddressCmd = "cat ${config.nix-bitcoin.onionAddresses.dataDir}/services/${service}"; }); diff --git a/modules/presets/secure-node.nix b/modules/presets/secure-node.nix index fb587fa..0318851 100644 --- a/modules/presets/secure-node.nix +++ b/modules/presets/secure-node.nix @@ -36,8 +36,6 @@ in { enable = true; listen = true; dataDirReadableByGroup = mkIf cfg.electrs.high-memory true; - discover = false; - addresstype = "bech32"; dbCache = 1000; }; diff --git a/modules/secrets/secrets.nix b/modules/secrets/secrets.nix index c44ee29..4776bca 100644 --- a/modules/secrets/secrets.nix +++ b/modules/secrets/secrets.nix @@ -13,7 +13,8 @@ let type = types.bool; default = false; description = '' - Set permissions for existing secrets in `nix-bitcoin.secretsDir`. + Set permissions for existing secrets in `nix-bitcoin.secretsDir` + before services are started. ''; }; @@ -21,7 +22,7 @@ let type = types.bool; default = false; description = '' - Automatically generate all required secrets at system startup. + Automatically generate all required secrets before services are started. Note: Make sure to create a backup of the generated secrets. ''; }; @@ -57,7 +58,7 @@ let }; permissions = mkOption { type = str; - default = "0440"; + default = "440"; }; }; } @@ -204,7 +205,9 @@ in { } # Make all other files accessible to root only - unprocessedFiles=$(comm -23 <(printf '%s\n' *) <(printf '%s\n' "''${processedFiles[@]}" | sort)) + unprocessedFiles=$( + comm -23 <(shopt -s nullglob; printf '%s\n' *) <(printf '%s\n' "''${processedFiles[@]}") + ) if [[ $unprocessedFiles ]]; then IFS=$'\n' chown root: $unprocessedFiles diff --git a/pkgs/lib.nix b/pkgs/lib.nix index c7a7480..61db1b0 100644 --- a/pkgs/lib.nix +++ b/pkgs/lib.nix @@ -83,4 +83,17 @@ let self = { map = [ map ]; version = 3; }; + + # Convert a bind address, which may be a special INADDR_ANY address, + # to an actual IP address + address = addr: + if addr == "0.0.0.0" then + "127.0.0.1" + else if addr == "::" then + "::1" + else + addr; + + addressWithPort = addr: port: "${self.address addr}:${toString port}"; + }; in self diff --git a/test/lib/make-test.nix b/test/lib/make-test.nix index ab1fec3..e7cf84f 100644 --- a/test/lib/make-test.nix +++ b/test/lib/make-test.nix @@ -42,6 +42,7 @@ name: testConfig: builtins.concatStringsSep "\n\n" [ initData (builtins.readFile ./../tests.py) + cfg.test.extraTestScript # Don't run tests in interactive mode. # is_interactive is set in ../run-tests.sh '' diff --git a/test/lib/test-lib.nix b/test/lib/test-lib.nix index f34c5d9..1de2b18 100644 --- a/test/lib/test-lib.nix +++ b/test/lib/test-lib.nix @@ -19,7 +19,11 @@ with lib; dictionary variable 'test_data'. The data is exported via JSON. ''; }; - + extraTestScript = mkOption { + type = types.lines; + default = ""; + description = "Extra lines added to the Python test script."; + }; container = { # Forwarded to extra-container. For descriptions, see # https://github.com/erikarvstedt/extra-container/blob/master/eval-config.nix diff --git a/test/tests.nix b/test/tests.nix index f68374e..4e93ae4 100644 --- a/test/tests.nix +++ b/test/tests.nix @@ -98,6 +98,10 @@ let txfee = 200; }; + # Disable restarting joinmarket-ob-watcher because it always fails + # on non-synced mainnet nodes + systemd.services.joinmarket-ob-watcher.serviceConfig.Restart = mkForce "no"; + tests.nodeinfo = config.nix-bitcoin.nodeinfo.enable; tests.backups = cfg.backups.enable; @@ -225,7 +229,8 @@ let services.bitcoind.regtest = true; systemd.services.bitcoind.postStart = mkAfter '' cli=${config.services.bitcoind.cli}/bin/bitcoin-cli - $cli createwallet "test" + # Don't fail when wallet already exists + $cli createwallet "test" || true address=$($cli getnewaddress) $cli generatetoaddress 10 $address '';