diff --git a/README.md b/README.md index e98cd28..ea801e4 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ NixOS modules * [summary](https://github.com/lightningd/plugins/tree/master/summary): print a nice summary of the node status * [zmq](https://github.com/lightningd/plugins/tree/master/zmq): publishes notifications via ZeroMQ to configured endpoints * [lnd](https://github.com/lightningnetwork/lnd) with support for announcing an onion service + * [lndconnect](https://github.com/LN-Zap/lndconnect) via a REST onion service * [spark-wallet](https://github.com/shesek/spark-wallet) * [electrs](https://github.com/romanz/electrs) * [btcpayserver](https://github.com/btcpayserver/btcpayserver) @@ -82,7 +83,7 @@ Security --- * **Simplicity:** Only services you select in `configuration.nix` and their dependencies are installed, packages and dependencies are [pinned](pkgs/nixpkgs-pinned.nix), 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](modules/nix-bitcoin-services.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. +* **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. Note that if the machine you're deploying *from* is insecure, there is nothing nix-bitcoin can do to protect itself. diff --git a/docs/usage.md b/docs/usage.md index 5d3985d..5631f85 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -58,6 +58,47 @@ Connect to spark-wallet Done ``` +Connect to LND with Zeus +--- +### Requirements +* Android phone +* [Orbot](https://guardianproject.info/apps/orbot/) installed from + [F-Droid](https://guardianproject.info/fdroid) (recommended) or + [Google Play](https://play.google.com/store/apps/details?id=org.torproject.android&hl=en) +* [Zeus](https://zeusln.app/) installed from + [F-Droid](https://f-droid.org/en/packages/app.zeusln.zeus/) (recommended) or + [Google Play](https://play.google.com/store/apps/details?id=app.zeusln.zeus) + +1. Enable `restOnionService` in `configuration.nix` + + Change + ``` + # services.lnd.restOnionService.enable = true; + ``` + to + ``` + services.lnd.restOnionService.enable = true; + ``` + +2. Deploy new `configuration.nix` + + ``` + nixops deploy -d bitcoin-node + ``` + +3. Run command `lndconnect-rest-onion` (under `operator` user) to create a QR code for + connecting to LND via the REST onion service. + +4. Enable Orbot VPN for Zeus + ``` + Open Orbot app + Turn on "VPN Mode" + Select Gear icon under "Tor-Enabled Apps" + Toggle checkbox under Zeus icon + ``` + +5. Scan the QR code with your Zeus wallet and start sending Satoshis privately + Connect to electrs --- ### Requirements Android diff --git a/examples/configuration.nix b/examples/configuration.nix index 5d79067..7452eff 100644 --- a/examples/configuration.nix +++ b/examples/configuration.nix @@ -63,6 +63,12 @@ # The onion service is automatically announced to peers. # nix-bitcoin.onionServices.lnd.public = true; # + # Set this to create an lnd REST onion service. + # Adds binary `lndconnect-rest-onion` to the system environment. + # This binary generates QR codes or URIs for connecting applications to lnd via the + # REST onion service. + # services.lnd.restOnionService.enable = true; + # ## WARNING # If you use lnd, you should manually backup your wallet mnemonic # seed. This will allow you to recover on-chain funds. You can run the diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index 7518606..af84cc8 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.bitcoind; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; secretsDir = config.nix-bitcoin.secretsDir; configFile = pkgs.writeText "bitcoin.conf" '' @@ -291,7 +291,7 @@ in { ''; description = "Binary to connect with the bitcoind instance."; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; }; @@ -348,7 +348,7 @@ in { install -o '${cfg.user}' -g '${cfg.group}' -m 640 <(echo "$cfg") $confFile fi ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { Type = "notify"; NotifyAccess = "all"; User = "${cfg.user}"; @@ -359,9 +359,9 @@ in { UMask = mkIf cfg.dataDirReadableByGroup "0027"; ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP) - // optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nix-bitcoin-services.allowAnyProtocol; + then nbLib.allowTor + else nbLib.allowAnyIP) + // optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nbLib.allowAnyProtocol; }; # Use this to update the banlist: @@ -382,11 +382,11 @@ in { fi done ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { User = "${cfg.user}"; Group = "${cfg.group}"; ReadWritePaths = "${cfg.dataDir}"; - } // nix-bitcoin-services.allowTor; + } // nbLib.allowTor; }; users.users.${cfg.user} = { diff --git a/modules/btcpayserver.nix b/modules/btcpayserver.nix index 3600930..b663132 100644 --- a/modules/btcpayserver.nix +++ b/modules/btcpayserver.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; nbPkgs = config.nix-bitcoin.pkgs; in { options.services = { @@ -44,7 +44,7 @@ in { internal = true; default = cfg.btcpayserver.enable; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; btcpayserver = { @@ -90,7 +90,7 @@ in { example = "btcpayserver"; description = "The prefix for root-relative btcpayserver URLs."; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; }; @@ -132,7 +132,7 @@ in { echo "btcrpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-btcpayserver)" \ >> '${cfg.nbxplorer.dataDir}/settings.config' ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { ExecStart = '' ${cfg.nbxplorer.package}/bin/nbxplorer --conf=${cfg.nbxplorer.dataDir}/settings.config \ --datadir=${cfg.nbxplorer.dataDir} @@ -143,8 +143,8 @@ in { ReadWritePaths = cfg.nbxplorer.dataDir; MemoryDenyWriteExecute = "false"; } // (if cfg.nbxplorer.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP + then nbLib.allowTor + else nbLib.allowAnyIP ); }; @@ -181,7 +181,7 @@ in { } >> ${cfg.btcpayserver.dataDir}/settings.config ''} ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { ExecStart = '' ${cfg.btcpayserver.package}/bin/btcpayserver --conf=${cfg.btcpayserver.dataDir}/settings.config \ --datadir=${cfg.btcpayserver.dataDir} @@ -192,8 +192,8 @@ in { ReadWritePaths = cfg.btcpayserver.dataDir; MemoryDenyWriteExecute = "false"; } // (if cfg.btcpayserver.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP + then nbLib.allowTor + else nbLib.allowAnyIP ); }; in self; diff --git a/modules/clightning.nix b/modules/clightning.nix index 544782b..d8838ec 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.clightning; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; nbPkgs = config.nix-bitcoin.pkgs; network = config.services.bitcoind.makeNetworkName "bitcoin" "regtest"; configFile = pkgs.writeText "config" '' @@ -91,7 +91,7 @@ in { If left empty, no address is announced. ''; }; - inherit (nix-bitcoin-services) enforceTor; + inherit (nbLib) enforceTor; }; config = mkIf cfg.enable { @@ -135,15 +135,15 @@ in { } >> '${cfg.dataDir}/config' ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; User = "${cfg.user}"; Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP + then nbLib.allowTor + else nbLib.allowAnyIP ); # Wait until the rpc socket appears postStart = '' diff --git a/modules/electrs.nix b/modules/electrs.nix index 5d03c10..a2c8102 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -3,7 +3,7 @@ with lib; let cfg = config.services.electrs; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; secretsDir = config.nix-bitcoin.secretsDir; bitcoind = config.services.bitcoind; in { @@ -51,7 +51,7 @@ in { default = ""; description = "Extra command line arguments passed to electrs."; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; config = mkIf cfg.enable { @@ -76,7 +76,7 @@ in { echo "cookie = \"${bitcoind.rpc.users.public.name}:$(cat ${secretsDir}/bitcoin-rpcpassword-public)\"" \ > electrs.toml ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { RuntimeDirectory = "electrs"; RuntimeDirectoryMode = "700"; WorkingDirectory = "/run/electrs"; @@ -104,8 +104,8 @@ in { RestartSec = "10s"; ReadWritePaths = "${cfg.dataDir} ${if cfg.high-memory then "${bitcoind.dataDir}" else ""}"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP + then nbLib.allowTor + else nbLib.allowAnyIP ); }; diff --git a/modules/joinmarket-ob-watcher.nix b/modules/joinmarket-ob-watcher.nix index e65bd7d..e8b2a30 100644 --- a/modules/joinmarket-ob-watcher.nix +++ b/modules/joinmarket-ob-watcher.nix @@ -3,7 +3,7 @@ with lib; let cfg = config.services.joinmarket-ob-watcher; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; nbPkgs = config.nix-bitcoin.pkgs; torAddress = builtins.head (builtins.split ":" config.services.tor.client.socksListenAddress); configFile = builtins.toFile "config" '' @@ -76,7 +76,7 @@ in { preStart = '' ln -snf ${configFile} ${cfg.dataDir}/joinmarket.cfg ''; - serviceConfig = nix-bitcoin-services.defaultHardening // rec { + serviceConfig = nbLib.defaultHardening // rec { StateDirectory = "joinmarket-ob-watcher"; StateDirectoryMode = "0770"; WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir @@ -87,7 +87,7 @@ in { User = cfg.user; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.allowTor; + } // nbLib.allowTor; }; users.users.${cfg.user} = { diff --git a/modules/joinmarket.nix b/modules/joinmarket.nix index d1c2367..ccdc8cb 100644 --- a/modules/joinmarket.nix +++ b/modules/joinmarket.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.joinmarket; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; nbPkgs = config.nix-bitcoin.pkgs; secretsDir = config.nix-bitcoin.secretsDir; @@ -137,7 +137,7 @@ in { readOnly = true; default = true; }; - inherit (nix-bitcoin-services) cliExec; + inherit (nbLib) cliExec; }; config = mkIf cfg.enable (mkMerge [{ @@ -177,15 +177,15 @@ in { requires = [ "bitcoind.service" ]; after = [ "bitcoind.service" ]; path = [ pkgs.sudo ]; - serviceConfig = nix-bitcoin-services.defaultHardening // { - ExecStartPre = nix-bitcoin-services.privileged '' + serviceConfig = nbLib.defaultHardening // { + ExecStartPre = nbLib.privileged '' 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") (nix-bitcoin-services.privileged '' + ExecStartPost = mkIf (bitcoind.network == "mainnet") (nbLib.privileged '' walletname=wallet.jmdat pw=$(cat "${secretsDir}"/jm-wallet-password) mnemonic=${secretsDir}/jm-wallet-seed @@ -207,7 +207,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = "${cfg.dataDir}"; - } // nix-bitcoin-services.allowTor; + } // nbLib.allowTor; }; nix-bitcoin.secrets.jm-wallet-password.user = cfg.user; @@ -239,14 +239,14 @@ in { pw=$(cat "${secretsDir}"/jm-wallet-password) echo "echo -n $pw | ${start}" > $RUNTIME_DIRECTORY/start ''; - serviceConfig = nix-bitcoin-services.defaultHardening // rec { + 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 ExecStart = "${pkgs.bash}/bin/bash /run/${RuntimeDirectory}/start"; User = "${cfg.user}"; ReadWritePaths = "${cfg.dataDir}"; - } // nix-bitcoin-services.allowTor; + } // nbLib.allowTor; }; }) ]); diff --git a/modules/lightning-loop.nix b/modules/lightning-loop.nix index 37d9448..fd87b19 100644 --- a/modules/lightning-loop.nix +++ b/modules/lightning-loop.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.lightning-loop; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; secretsDir = config.nix-bitcoin.secretsDir; network = config.services.bitcoind.network; rpclisten = "${cfg.rpcAddress}:${toString cfg.rpcPort}"; @@ -80,7 +80,7 @@ in { ''; description = "Binary to connect with the lightning-loop instance."; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; config = mkIf cfg.enable { @@ -96,15 +96,15 @@ in { wantedBy = [ "multi-user.target" ]; requires = [ "lnd.service" ]; after = [ "lnd.service" ]; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { ExecStart = "${cfg.package}/bin/loopd --configfile=${configFile}"; User = "lnd"; Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP); + then nbLib.allowTor + else nbLib.allowAnyIP); }; nix-bitcoin.secrets = { diff --git a/modules/liquid.nix b/modules/liquid.nix index 6e83321..26b9e5a 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.liquidd; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; nbPkgs = config.nix-bitcoin.pkgs; secretsDir = config.nix-bitcoin.secretsDir; pidFile = "${cfg.dataDir}/liquidd.pid"; @@ -203,7 +203,7 @@ in { ''; description = "Binary for managing liquid swaps."; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; }; @@ -232,7 +232,7 @@ in { echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf' echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword-public)" >> '${cfg.dataDir}/elements.conf' ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { Type = "simple"; User = "${cfg.user}"; Group = "${cfg.group}"; @@ -241,8 +241,8 @@ in { Restart = "on-failure"; ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP + then nbLib.allowTor + else nbLib.allowAnyIP ); }; diff --git a/modules/lnd-rest-onion-service.nix b/modules/lnd-rest-onion-service.nix new file mode 100644 index 0000000..8e182f1 --- /dev/null +++ b/modules/lnd-rest-onion-service.nix @@ -0,0 +1,51 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + cfg = config.services.lnd.restOnionService; + nbLib = config.nix-bitcoin.lib; + secretsDir = config.nix-bitcoin.secretsDir; + + lnd = config.services.lnd; + + bin = pkgs.writeScriptBin "lndconnect-rest-onion" '' + #!/usr/bin/env -S sudo -u lnd ${pkgs.bash}/bin/bash + + exec ${cfg.package}/bin/lndconnect \ + --host=$(cat ${config.nix-bitcoin.onionAddresses.dataDir}/lnd/lnd-rest) \ + --port=${toString lnd.restPort} \ + --lnddir=${lnd.dataDir} \ + --tlscertpath=${secretsDir}/lnd-cert "$@" + ''; +in { + options.services.lnd.restOnionService = { + enable = mkOption { + default = false; + type = types.bool; + description = '' + Create an onion service for the lnd REST service. + Add a `lndconnect-rest-onion` binary (https://github.com/LN-Zap/lndconnect) to the system environment. + This binary generates QR codes or URIs for connecting applications to lnd via the REST onion service. + ''; + }; + package = mkOption { + type = types.package; + default = config.nix-bitcoin.pkgs.lndconnect; + description = "The package providing lndconnect binaries."; + }; + }; + + config = mkIf cfg.enable { + services.tor = { + enable = true; + hiddenServices.lnd-rest = nbLib.mkHiddenService { + toHost = lnd.restAddress; + port = lnd.restPort; + }; + }; + nix-bitcoin.onionAddresses.access.lnd = [ "lnd-rest" ]; + + environment.systemPackages = [ bin ]; + }; +} diff --git a/modules/lnd.nix b/modules/lnd.nix index e8fb9c7..4899dac 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.lnd; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; secretsDir = config.nix-bitcoin.secretsDir; bitcoind = config.services.bitcoind; @@ -144,7 +144,7 @@ in { If left empty, no address is announced. ''; }; - inherit (nix-bitcoin-services) enforceTor; + inherit (nbLib) enforceTor; }; config = mkIf cfg.enable { @@ -186,7 +186,7 @@ in { ''} } >> '${cfg.dataDir}/lnd.conf' ''; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { RuntimeDirectory = "lnd"; # Only used to store custom macaroons RuntimeDirectoryMode = "711"; ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf"; @@ -198,7 +198,7 @@ in { restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1"; in [ # Run fully privileged for secrets dir write access - "+${nix-bitcoin-services.script '' + "+${nbLib.script '' 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; } @@ -215,7 +215,7 @@ in { fi chown lnd: "$mnemonic" ''}" - "${nix-bitcoin-services.script '' + "${nbLib.script '' if [[ ! -f ${networkDir}/wallet.db ]]; then echo Create lnd wallet @@ -248,7 +248,7 @@ in { ''}" # Run fully privileged for chown - "+${nix-bitcoin-services.script '' + "+${nbLib.script '' umask ug=r,o= ${lib.concatMapStrings (macaroon: '' echo "Create custom macaroon ${macaroon}" @@ -265,9 +265,9 @@ in { ''}" ]; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP - ) // nix-bitcoin-services.allowAnyProtocol; # For ZMQ + then nbLib.allowTor + else nbLib.allowAnyIP + ) // nbLib.allowAnyProtocol; # For ZMQ }; users.users.lnd = { diff --git a/modules/modules.nix b/modules/modules.nix index 6ca7e18..548bc37 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -13,6 +13,7 @@ with lib; ./clightning-plugins ./spark-wallet.nix ./lnd.nix + ./lnd-rest-onion-service.nix ./lightning-loop.nix ./btcpayserver.nix ./electrs.nix @@ -35,17 +36,17 @@ with lib; disabledModules = [ "services/networking/bitcoind.nix" ]; options = { - nix-bitcoin-services = mkOption { - readOnly = true; - default = import ./nix-bitcoin-services.nix lib pkgs; - }; - nix-bitcoin = { pkgs = mkOption { type = types.attrs; default = (import ../pkgs { inherit pkgs; }).modulesPkgs; }; + lib = mkOption { + readOnly = true; + default = import ../pkgs/lib.nix lib pkgs; + }; + # Torify binary that works with custom Tor SOCKS addresses # Related issue: https://github.com/NixOS/nixpkgs/issues/94236 torify = mkOption { diff --git a/modules/onion-addresses.nix b/modules/onion-addresses.nix index df9c9dc..64cda59 100644 --- a/modules/onion-addresses.nix +++ b/modules/onion-addresses.nix @@ -10,7 +10,7 @@ with lib; let cfg = config.nix-bitcoin.onionAddresses; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; in { options.nix-bitcoin.onionAddresses = { access = mkOption { @@ -47,7 +47,7 @@ in { wantedBy = [ "tor.service" ]; bindsTo = [ "tor.service" ]; after = [ "tor.service" ]; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { Type = "oneshot"; RemainAfterExit = true; StateDirectory = "onion-addresses"; diff --git a/modules/onion-services.nix b/modules/onion-services.nix index 52c6c2f..288e673 100644 --- a/modules/onion-services.nix +++ b/modules/onion-services.nix @@ -10,6 +10,7 @@ with lib; let cfg = config.nix-bitcoin.onionServices; + nbLib = config.nix-bitcoin.lib; services = builtins.attrNames cfg; @@ -60,13 +61,10 @@ in { let service = config.services.${name}; inherit (cfg.${name}) externalPort; - in { - map = [{ - port = if externalPort != null then externalPort else service.port; - toPort = service.port; - toHost = if service.address == "0.0.0.0" then "127.0.0.1" else service.address; - }]; - version = 3; + in nbLib.mkHiddenService { + port = if externalPort != null then externalPort else service.port; + toPort = service.port; + toHost = if service.address == "0.0.0.0" then "127.0.0.1" else service.address; } ); }; diff --git a/modules/presets/secure-node.nix b/modules/presets/secure-node.nix index a0472c4..cf09b60 100644 --- a/modules/presets/secure-node.nix +++ b/modules/presets/secure-node.nix @@ -4,13 +4,8 @@ with lib; let cfg = config.services; - + nbLib = config.nix-bitcoin.lib; operatorName = config.nix-bitcoin.operator.name; - - mkHiddenService = map: { - map = [ map ]; - version = 3; - }; in { imports = [ ../modules.nix @@ -30,7 +25,7 @@ in { ]; # sshd - services.tor.hiddenServices.sshd = mkHiddenService { port = 22; }; + services.tor.hiddenServices.sshd = nbLib.mkHiddenService { port = 22; }; nix-bitcoin.onionAddresses.access.${operatorName} = [ "sshd" ]; services.bitcoind = { diff --git a/modules/recurring-donations.nix b/modules/recurring-donations.nix index 93ae7b5..84ebea9 100644 --- a/modules/recurring-donations.nix +++ b/modules/recurring-donations.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.recurring-donations; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; recurring-donations-script = pkgs.writeScript "recurring-donations.sh" '' LNCLI="${config.nix-bitcoin.pkgs.clightning}/bin/lightning-cli --lightning-dir=${config.services.clightning.dataDir}" pay_tallycoin() { @@ -75,7 +75,7 @@ in { Random delay to add to scheduled time for donation. Default is one day. ''; }; - enforceTor = nix-bitcoin-services.enforceTor; + enforceTor = nbLib.enforceTor; }; config = mkIf cfg.enable { @@ -93,13 +93,13 @@ in { requires = [ "clightning.service" ]; after = [ "clightning.service" ]; path = with pkgs; [ nix-bitcoin.clightning curl sudo jq ]; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { ExecStart = "${pkgs.bash}/bin/bash ${recurring-donations-script}"; User = "recurring-donations"; Type = "oneshot"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP); + then nbLib.allowTor + else nbLib.allowAnyIP); }; systemd.timers.recurring-donations = { requires = [ "clightning.service" ]; diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index a130dd2..3a46f89 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -4,7 +4,7 @@ with lib; let cfg = config.services.spark-wallet; - inherit (config) nix-bitcoin-services; + nbLib = config.nix-bitcoin.lib; # Use wasabi rate provider because the default (bitstamp) doesn't accept # connections through Tor @@ -54,7 +54,7 @@ in { encodes an URL for accessing the web interface. ''; }; - inherit (nix-bitcoin-services) enforceTor; + inherit (nbLib) enforceTor; }; config = mkIf cfg.enable { @@ -73,14 +73,14 @@ in { requires = [ "clightning.service" ]; after = [ "clightning.service" ]; script = startScript; - serviceConfig = nix-bitcoin-services.defaultHardening // { + serviceConfig = nbLib.defaultHardening // { User = "spark-wallet"; Restart = "on-failure"; RestartSec = "10s"; } // (if cfg.enforceTor - then nix-bitcoin-services.allowTor - else nix-bitcoin-services.allowAnyIP) - // nix-bitcoin-services.nodejs; + then nbLib.allowTor + else nbLib.allowAnyIP) + // nbLib.nodejs; }; nix-bitcoin.secrets.spark-wallet-login.user = "spark-wallet"; }; diff --git a/modules/nix-bitcoin-services.nix b/pkgs/lib.nix similarity index 90% rename from modules/nix-bitcoin-services.nix rename to pkgs/lib.nix index 24d0099..ab8bfb0 100644 --- a/modules/nix-bitcoin-services.nix +++ b/pkgs/lib.nix @@ -1,9 +1,9 @@ -# See `man systemd.exec` and `man systemd.resource-control` for an explanation -# of the various systemd options available through this module. - lib: pkgs: with lib; + +# See `man systemd.exec` and `man systemd.resource-control` for an explanation +# of the systemd-related options available through this module. let self = { # These settings roughly follow systemd's "strict" security profile defaultHardening = { @@ -46,8 +46,8 @@ let self = { type = types.bool; default = false; description = '' - "Whether to force Tor on a service by only allowing connections from and - to 127.0.0.1;"; + Whether to force Tor on a service by only allowing connections from and + to 127.0.0.1; ''; }; @@ -65,4 +65,9 @@ let self = { type = types.str; default = "exec"; }; + + mkHiddenService = map: { + map = [ map ]; + version = 3; + }; }; in self diff --git a/pkgs/pinned.nix b/pkgs/pinned.nix index 00c99ef..a1086b0 100644 --- a/pkgs/pinned.nix +++ b/pkgs/pinned.nix @@ -11,6 +11,7 @@ in bitcoind clightning lnd + lndconnect nbxplorer btcpayserver; diff --git a/test/tests.nix b/test/tests.nix index b7ce86e..b99462c 100644 --- a/test/tests.nix +++ b/test/tests.nix @@ -46,6 +46,8 @@ let testEnv = rec { tests.lnd = cfg.lnd.enable; services.lnd.port = 9736; + tests.lnd-rest-onion-service = cfg.lnd.restOnionService.enable; + tests.lightning-loop = cfg.lightning-loop.enable; tests.electrs = cfg.electrs.enable; @@ -115,6 +117,7 @@ let testEnv = rec { test.features.clightningPlugins = true; services.spark-wallet.enable = true; services.lnd.enable = true; + services.lnd.restOnionService.enable = true; services.lightning-loop.enable = true; services.electrs.enable = true; services.liquidd.enable = true; diff --git a/test/tests.py b/test/tests.py index 598bbd9..7832433 100644 --- a/test/tests.py +++ b/test/tests.py @@ -162,6 +162,11 @@ def _(): assert_no_failure("lnd") +@test("lnd-rest-onion-service") +def _(): + assert_matches("runuser -u operator -- lndconnect-rest-onion -j", ".onion") + + @test("lightning-loop") def _(): assert_running("lightning-loop")