diff --git a/examples/configuration.nix b/examples/configuration.nix index 85bd665..ee24f4f 100644 --- a/examples/configuration.nix +++ b/examples/configuration.nix @@ -4,7 +4,7 @@ { config, pkgs, lib, ... }: { imports = [ - + # FIXME: The hardened kernel profile improves security but # decreases performance by ~50%. @@ -18,15 +18,12 @@ #./hardware-configuration.nix ]; # FIXME: Enable modules by uncommenting their respective line. Disable - # modules by commenting out their respective line. Enable this module to - # use the nix-bitcoin node configuration. Only disable this if you know what - # you are doing. - services.nix-bitcoin.enable = true; + # modules by commenting out their respective line. ### BITCOIND # Bitcoind is enabled by default if nix-bitcoin is enabled # - # You can override default settings from nix-bitcoin.nix as follows + # You can override default settings from secure-node.nix as follows # services.bitcoind.prune = lib.mkForce 100000; # # You can add options that are not defined in modules/bitcoind.nix as follows diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index edc1db8..409d264 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -256,7 +256,7 @@ in { }; config = mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; + environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ]; systemd.services.bitcoind = { description = "Bitcoin daemon"; requires = [ "nix-bitcoin-secrets.target" ]; diff --git a/modules/clightning.nix b/modules/clightning.nix index 9a561c3..607f9e7 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -71,6 +71,7 @@ in { }; config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.nix-bitcoin.clightning (hiPrio cfg.cli) ]; users.users.clightning = { description = "clightning User"; group = "clightning"; diff --git a/modules/default.nix b/modules/default.nix index 6664741..2ca1ebe 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -7,7 +7,7 @@ lightning-charge = ./lightning-charge.nix; liquid = ./liquid.nix; nanopos = ./nanopos.nix; - nix-bitcoin = ./nix-bitcoin.nix; + presets.secure-node = ./presets/secure-node.nix; nix-bitcoin-webindex = ./nix-bitcoin-webindex.nix; spark-wallet = ./spark-wallet.nix; recurring-donations = ./recurring-donations.nix; diff --git a/modules/electrs.nix b/modules/electrs.nix index 4ca1cda..3d30187 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -44,11 +44,6 @@ in { default = 50001; description = "RPC port."; }; - onionport = mkOption { - type = types.ints.u16; - default = 50002; - description = "Port on which to listen for tor client connections."; - }; extraArgs = mkOption { type = types.separatedString " "; default = ""; @@ -66,6 +61,8 @@ in { }; config = mkIf cfg.enable (mkMerge [{ + environment.systemPackages = [ pkgs.nix-bitcoin.electrs ]; + systemd.services.electrs = { description = "Electrs Electrum Server"; wantedBy = [ "multi-user.target" ]; diff --git a/modules/hardware-wallets.nix b/modules/hardware-wallets.nix index 33d3a3c..8337e5c 100644 --- a/modules/hardware-wallets.nix +++ b/modules/hardware-wallets.nix @@ -32,11 +32,16 @@ in { }; config = mkMerge [ - { - # Create group + (mkIf (cfg.ledger || cfg.trezor) { + environment.systemPackages = with pkgs; [ + nix-bitcoin.hwi + # Provides lsusb for debugging + usbutils + ]; users.groups."${cfg.group}" = {}; - } + }) (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 { @@ -48,6 +53,7 @@ in { }); }) (mkIf cfg.trezor { + environment.systemPackages = [ pkgs.python3.pkgs.trezor ]; # Don't use rules from nixpkgs because we want to use our own group. services.udev.packages = lib.singleton (pkgs.writeTextFile { name = "trezord-udev-rules"; diff --git a/modules/lightning-charge.nix b/modules/lightning-charge.nix index 0d0061a..9566402 100644 --- a/modules/lightning-charge.nix +++ b/modules/lightning-charge.nix @@ -24,6 +24,7 @@ in { }; config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.nix-bitcoin.lightning-charge ]; systemd.services.lightning-charge = { description = "Run lightning-charge"; wantedBy = [ "multi-user.target" ]; diff --git a/modules/liquid.nix b/modules/liquid.nix index 3dce19a..5716230 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -195,7 +195,11 @@ in { }; config = mkIf cfg.enable { - environment.systemPackages = [ pkgs.nix-bitcoin.elementsd ]; + environment.systemPackages = [ + pkgs.nix-bitcoin.elementsd + (hiPrio cfg.cli) + (hiPrio cfg.swap-cli) + ]; systemd.services.liquidd = { description = "Elements daemon providing access to the Liquid sidechain"; requires = [ "bitcoind.service" ]; diff --git a/modules/lnd.nix b/modules/lnd.nix index 7cead2e..878a4ec 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -77,7 +77,7 @@ in { }; config = mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; + environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ]; systemd.services.lnd = { description = "Run LND"; path = [ pkgs.nix-bitcoin.bitcoind ]; diff --git a/modules/nanopos.nix b/modules/nanopos.nix index b34cf6d..2fa5894 100644 --- a/modules/nanopos.nix +++ b/modules/nanopos.nix @@ -52,6 +52,7 @@ in { }; config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.nix-bitcoin.nanopos ]; systemd.services.nanopos = { description = "Run nanopos"; wantedBy = [ "multi-user.target" ]; @@ -60,7 +61,6 @@ in { serviceConfig = { EnvironmentFile = "${config.nix-bitcoin.secretsDir}/nanopos-env"; ExecStart = "${pkgs.nix-bitcoin.nanopos}/bin/nanopos -y ${cfg.itemsFile} -p ${toString cfg.port} --show-bolt11"; - User = "nanopos"; Restart = "on-failure"; RestartSec = "10s"; diff --git a/modules/nix-bitcoin.nix b/modules/nix-bitcoin.nix index 1b71e42..64879d0 100644 --- a/modules/nix-bitcoin.nix +++ b/modules/nix-bitcoin.nix @@ -1,183 +1,9 @@ -{ config, lib, pkgs, ... }: +# This file exists only for backwards compatibility -with lib; - -let - cfg = config.services.nix-bitcoin; - operatorCopySSH = pkgs.writeText "operator-copy-ssh.sh" '' - mkdir -p ${config.users.users.operator.home}/.ssh - if [ -e "${config.users.users.root.home}/.vbox-nixops-client-key" ]; then - cp ${config.users.users.root.home}/.vbox-nixops-client-key ${config.users.users.operator.home}/.ssh/authorized_keys - fi - if [ -e "/etc/ssh/authorized_keys.d/root" ]; then - cat /etc/ssh/authorized_keys.d/root >> ${config.users.users.operator.home}/.ssh/authorized_keys - fi - chown -R operator ${config.users.users.operator.home}/.ssh - ''; -in { - imports = [ ./modules.nix ]; - - options.services.nix-bitcoin = { - enable = mkOption { - type = types.bool; - default = false; - description = '' - If enabled, the nix-bitcoin service will be installed. - ''; - }; - }; - - config = mkIf cfg.enable { - nix-bitcoin.secretsDir = mkDefault "/secrets"; - - networking.firewall.enable = true; - - # Tor - services.tor.enable = true; - services.tor.client.enable = true; - # LND uses ControlPort to create onion services - services.tor.controlPort = if config.services.lnd.enable then 9051 else null; - - # Tor SSH service - services.tor.hiddenServices.sshd = { - map = [{ - port = 22; - }]; - version = 3; - }; - - # bitcoind - services.bitcoind.enable = true; - services.bitcoind.listen = true; - services.bitcoind.sysperms = if config.services.electrs.enable then true else null; - services.bitcoind.disablewallet = if config.services.electrs.enable then true else null; - services.bitcoind.proxy = config.services.tor.client.socksListenAddress; - services.bitcoind.enforceTor = true; - services.bitcoind.port = 8333; - services.bitcoind.zmqpubrawblock = "tcp://127.0.0.1:28332"; - services.bitcoind.zmqpubrawtx = "tcp://127.0.0.1:28333"; - services.bitcoind.assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6"; - services.bitcoind.addnodes = [ "ecoc5q34tmbq54wl.onion" ]; - services.bitcoind.discover = false; - services.bitcoind.addresstype = "bech32"; - services.bitcoind.prune = 0; - services.bitcoind.dbCache = 1000; - services.tor.hiddenServices.bitcoind = { - map = [{ - port = config.services.bitcoind.port; - }]; - version = 3; - }; - - # clightning - services.clightning.bitcoin-rpcuser = config.services.bitcoind.rpcuser; - services.clightning.proxy = config.services.tor.client.socksListenAddress; - services.clightning.enforceTor = true; - services.clightning.always-use-proxy = true; - services.clightning.bind-addr = "127.0.0.1:9735"; - services.tor.hiddenServices.clightning = { - map = [{ - port = 9735; toPort = 9735; - }]; - version = 3; - }; - - # lnd - services.lnd.enforceTor = true; - - # Create user operator which can use bitcoin-cli and lightning-cli - users.users.operator = { - isNormalUser = true; - extraGroups = [ config.services.bitcoind.group ] - ++ (if config.services.clightning.enable then [ "clightning" ] else [ ]) - ++ (if config.services.lnd.enable then [ "lnd" ] else [ ]) - ++ (if config.services.liquidd.enable then [ config.services.liquidd.group ] else [ ]) - ++ (if (config.services.hardware-wallets.ledger || config.services.hardware-wallets.trezor) - then [ config.services.hardware-wallets.group ] else [ ]); - }; - # Give operator access to onion hostnames - services.onion-chef.enable = true; - services.onion-chef.access.operator = [ "bitcoind" "clightning" "nginx" "liquidd" "spark-wallet" "electrs" "sshd" ]; - - # Unfortunately c-lightning doesn't allow setting the permissions of the rpc socket - # https://github.com/ElementsProject/lightning/issues/1366 - security.sudo.configFile = - (optionalString config.services.clightning.enable '' - operator ALL=(clightning) NOPASSWD: ALL - '') + - (optionalString config.services.lnd.enable '' - operator ALL=(lnd) NOPASSWD: ALL - ''); - - # Give root ssh access to the operator account - systemd.services.copy-root-authorized-keys = { - description = "Copy root authorized keys"; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = "${pkgs.bash}/bin/bash \"${operatorCopySSH}\""; - user = "root"; - type = "oneshot"; - }; - }; - - services.nix-bitcoin-webindex.enforceTor = true; - - services.liquidd.rpcuser = "liquidrpc"; - services.liquidd.prune = 1000; - services.liquidd.extraConfig = " - mainchainrpcuser=${config.services.bitcoind.rpcuser} - mainchainrpcport=8332 - "; - services.liquidd.validatepegin = true; - services.liquidd.listen = true; - services.liquidd.proxy = config.services.tor.client.socksListenAddress; - services.liquidd.enforceTor = true; - services.liquidd.port = 7042; - services.tor.hiddenServices.liquidd = { - map = [{ - port = config.services.liquidd.port; toPort = config.services.liquidd.port; - }]; - version = 3; - }; - - services.spark-wallet.onion-service = true; - services.electrs.port = 50001; - services.electrs.enforceTor = true; - services.electrs.onionport = 50002; - services.electrs.TLSProxy.enable = true; - services.electrs.TLSProxy.port = 50003; - services.tor.hiddenServices.electrs = { - map = [{ - port = config.services.electrs.onionport; toPort = config.services.electrs.TLSProxy.port; - }]; - version = 3; - }; - environment.systemPackages = with pkgs; with nix-bitcoin; let - s = config.services; - in - [ - tor - bitcoind - (hiPrio s.bitcoind.cli) - nodeinfo - jq - qrencode - ] - ++ optionals s.clightning.enable [clightning (hiPrio s.clightning.cli)] - ++ optionals s.lnd.enable [lnd (hiPrio s.lnd.cli)] - ++ optionals s.lightning-charge.enable [lightning-charge] - ++ optionals s.nanopos.enable [nanopos] - ++ optionals s.nix-bitcoin-webindex.enable [nginx] - ++ optionals s.liquidd.enable [elementsd (hiPrio s.liquidd.cli) (hiPrio s.liquidd.swap-cli)] - ++ optionals s.spark-wallet.enable [spark-wallet] - ++ optionals s.electrs.enable [electrs] - ++ optionals (s.hardware-wallets.ledger || s.hardware-wallets.trezor) [ - hwi - # To allow debugging issues with lsusb - usbutils - ] - ++ optionals s.hardware-wallets.trezor [ - python3.pkgs.trezor - ]; - }; +{ lib, ... }: +{ + imports = [ + ./presets/secure-node.nix + (lib.mkRemovedOptionModule [ "services" "nix-bitcoin" "enable" ] "Please directly import ./presets/secure-node.nix") + ] } diff --git a/modules/presets/secure-node.nix b/modules/presets/secure-node.nix new file mode 100644 index 0000000..0d2b666 --- /dev/null +++ b/modules/presets/secure-node.nix @@ -0,0 +1,154 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services; + + mkHiddenService = map: { + map = [ map ]; + version = 3; + }; +in { + imports = [ ../modules.nix ]; + + options = { + services.clightning.onionport = mkOption { + type = types.ints.u16; + default = 9735; + description = "Port on which to listen for tor client connections."; + }; + + services.electrs.onionport = mkOption { + type = types.ints.u16; + default = 50002; + description = "Port on which to listen for tor client connections."; + }; + }; + + config = { + # For backwards compatibility only + nix-bitcoin.secretsDir = mkDefault "/secrets"; + + networking.firewall.enable = true; + + # Tor + services.tor = { + enable = true; + client.enable = true; + # LND uses ControlPort to create onion services + controlPort = mkIf cfg.lnd.enable 9051; + + hiddenServices.sshd = mkHiddenService { port = 22; }; + }; + + # bitcoind + services.bitcoind = { + enable = true; + listen = true; + sysperms = if cfg.electrs.enable then true else null; + disablewallet = if cfg.electrs.enable then true else null; + proxy = cfg.tor.client.socksListenAddress; + enforceTor = true; + port = 8333; + zmqpubrawblock = "tcp://127.0.0.1:28332"; + zmqpubrawtx = "tcp://127.0.0.1:28333"; + assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6"; + addnodes = [ "ecoc5q34tmbq54wl.onion" ]; + discover = false; + addresstype = "bech32"; + prune = 0; + dbCache = 1000; + }; + services.tor.hiddenServices.bitcoind = mkHiddenService { port = cfg.bitcoind.port; }; + + # clightning + services.clightning = { + bitcoin-rpcuser = cfg.bitcoind.rpcuser; + proxy = cfg.tor.client.socksListenAddress; + enforceTor = true; + always-use-proxy = true; + bind-addr = "127.0.0.1:${toString cfg.clightning.onionport}"; + }; + services.tor.hiddenServices.clightning = mkHiddenService { port = cfg.clightning.onionport; }; + + # lnd + services.lnd.enforceTor = true; + + # liquidd + services.liquidd = { + rpcuser = "liquidrpc"; + prune = 1000; + extraConfig = '' + mainchainrpcuser=${cfg.bitcoind.rpcuser} + mainchainrpcport=8332 + ''; + validatepegin = true; + listen = true; + proxy = cfg.tor.client.socksListenAddress; + enforceTor = true; + port = 7042; + }; + services.tor.hiddenServices.liquidd = mkHiddenService { port = cfg.liquidd.port; }; + + # electrs + services.electrs = { + port = 50001; + enforceTor = true; + TLSProxy.enable = true; + TLSProxy.port = 50003; + }; + services.tor.hiddenServices.electrs = mkHiddenService { + port = cfg.electrs.onionport; + toPort = cfg.electrs.TLSProxy.port; + }; + + services.spark-wallet.onion-service = true; + + services.nix-bitcoin-webindex.enforceTor = true; + + + environment.systemPackages = with pkgs; [ + tor + jq + qrencode + nix-bitcoin.nodeinfo + ]; + + # Create user 'operator' which can access the node's services + users.users.operator = { + isNormalUser = true; + extraGroups = [ + "systemd-journal" + cfg.bitcoind.group + ] + ++ (optionals cfg.clightning.enable [ "clightning" ]) + ++ (optionals cfg.lnd.enable [ "lnd" ]) + ++ (optionals cfg.liquidd.enable [ cfg.liquidd.group ]) + ++ (optionals (cfg.hardware-wallets.ledger || cfg.hardware-wallets.trezor) + [ cfg.hardware-wallets.group ]); + openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys; + }; + # Give operator access to onion hostnames + services.onion-chef.enable = true; + services.onion-chef.access.operator = [ "bitcoind" "clightning" "nginx" "liquidd" "spark-wallet" "electrs" "sshd" ]; + + # Unfortunately c-lightning doesn't allow setting the permissions of the rpc socket + # https://github.com/ElementsProject/lightning/issues/1366 + security.sudo.configFile = + (optionalString cfg.clightning.enable '' + operator ALL=(clightning) NOPASSWD: ALL + '') + + (optionalString cfg.lnd.enable '' + operator ALL=(lnd) NOPASSWD: ALL + ''); + + # Enable nixops ssh for operator (`nixops ssh operator@mynode`) on nixops-vbox deployments + systemd.services.get-vbox-nixops-client-key = + mkIf (builtins.elem ".vbox-nixops-client-key" config.services.openssh.authorizedKeysFiles) { + postStart = '' + cp "${config.users.users.root.home}/.vbox-nixops-client-key" "${config.users.users.operator.home}" + ''; + }; + }; +} diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index c1565ea..13a5cca 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -47,6 +47,7 @@ in { }; config = mkIf cfg.enable { + environment.systemPackages = [ pkgs.nix-bitcoin.spark-wallet ]; services.tor.enable = cfg.onion-service; # requires client functionality for Bitcoin rate lookup services.tor.client.enable = true; diff --git a/test/test.nix b/test/test.nix index 918ce38..d609d3d 100644 --- a/test/test.nix +++ b/test/test.nix @@ -10,13 +10,12 @@ import ./make-test.nix rec { machine = { pkgs, lib, ... }: with lib; { imports = [ - ../modules/nix-bitcoin.nix + ../modules/presets/secure-node.nix ../modules/secrets/generate-secrets.nix # using the hardened profile increases total test duration by ~50%, so disable it for now # hardened ]; - services.nix-bitcoin.enable = true; services.bitcoind.extraConfig = mkForce "connect=0"; services.clightning.enable = true;