diff --git a/README.md b/README.md index 2956de9..e123ea4 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,14 @@ Get started - To add nix-bitcoin to an existing NixOS configuration, see [importable-configuration.nix](examples/importable-configuration.nix) and the [Flake example](examples/flakes/flake.nix). +Docs +--- +* [Hardware Requirements](docs/hardware.md) +* [Installation](docs/install.md) +* [Configuration and maintenance](docs/configuration.md) +* [Using services](docs/services.md) +* [FAQ](docs/faq.md) + Features --- A [configuration preset](modules/presets/secure-node.nix) for setting up a secure node @@ -69,13 +77,13 @@ NixOS modules ([src](modules/modules.nix)) * [Lightning Pool](https://github.com/lightninglabs/pool) * [charge-lnd](https://github.com/accumulator/charge-lnd): policy-based channel fee manager * [lndconnect](https://github.com/LN-Zap/lndconnect) via a REST onion service + * [Ride The Lightning](https://github.com/Ride-The-Lightning/RTL): web interface for `lnd` and `clightning` * [spark-wallet](https://github.com/shesek/spark-wallet) * [electrs](https://github.com/romanz/electrs) * [btcpayserver](https://github.com/btcpayserver/btcpayserver) * [liquid](https://github.com/elementsproject/elements) * [JoinMarket](https://github.com/joinmarket-org/joinmarket-clientserver) * [JoinMarket Orderbook Watcher](https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master/docs/orderbook.md) - * [recurring-donations](modules/recurring-donations.nix): for periodic lightning payments * [bitcoin-core-hwi](https://github.com/bitcoin-core/HWI) * Helper * [netns-isolation](modules/netns-isolation.nix): isolates applications on the network-level via network namespaces @@ -92,13 +100,6 @@ Security Note that if the machine you're deploying *from* is insecure, there is nothing nix-bitcoin can do to protect itself. -Docs ---- -* [Hardware Requirements](docs/hardware.md) -* [Install instructions](docs/install.md) -* [Usage instructions](docs/usage.md) -* [FAQ](docs/faq.md) - Troubleshooting --- If you are having problems with nix-bitcoin check the [FAQ](docs/faq.md) or submit an issue.\ diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..fa872d5 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,240 @@ +# Managing your deployment + +This section applies to users of the deployment method described in the [installation guide](./install.md). + +## Deployment shell +Run command `nix-shell` in your deployment directory.\ +You now have access to deployment commands: + +- `deploy`\ + Deploy the current configuration to your node. +- `eval-config`\ + Locally evaluate the configuration. This is useful to check for configuration errors. +- `h`, `help`\ + Show help + +## Updating nix-bitcoin +Run `update-nix-bitcoin` from the deployment shell.\ +This fetches the latest release, verifies its signatures and updates `nix-bitcoin-release.nix`. + +# Customizing your configuration + +## Get started with Nix + +See [Nix - A One Pager](https://github.com/tazjin/nix-1p) for a short guide +to Nix, the language used in `configuration.nix`. + +You can follow along this guide by running command `nix repl` which allows you to interactively +evaluate Nix expressions. + +For a general introduction to the Nix and NixOS ecosystem, see [nix.dev](https://nix.dev/). + +## Set options + +All features and services are configurable through options. You can find a list of +supported options at the top of each nix-bitcoin [module](../modules/modules.nix) +(Examples: [bitcoind.nix](../modules/bitcoind.nix), [btcpayserver.nix](../modules/btcpayserver.nix)). + +Example: Set some `bitcoind` options by adding these lines to your `configuration.nix`: +```nix +# Use a custom data dir +services.bitcoind.dataDir = "/my/datadir"; + +# Enable txindex +services.bitcoind.txindex = true; +``` + +You can also use regular [NixOS options](https://search.nixos.org/options) +for configuring your system: +```nix +networking.hostName = "myhost"; +time.timeZone = "UTC"; +``` + +## Debug your configuration + +To print the values of specific options of your config, add the following to your `configuration.nix`: +```nix +system.extraDependencies = let + debugVal = config.networking.firewall.allowedTCPPorts; + # More example options: + # debugVal = config.environment.systemPackages; + # debugVal = config.services.bitcoind.dataDir; +in lib.traceSeqN 3 debugVal []; +``` +and run command `eval-config` from the deployment shell. + +# Allowing external connections to services + +## Allow peer connections to bitcoind + +```nix +services.bitcoind = { + # Accept incoming peer connections + listen = true; + + # Listen to connections on all interfaces + address = "0.0.0.0"; + + # Set this to also add IPv6 connectivity. + extraConfig = '' + bind=:: + ''; + + # If you're using the `secure-node.nix` template, set this to allow non-Tor connections + # to bitcoind + tor.enforce = false; + # Also set this if bitcoind should not use Tor for outgoing peer connections + tor.proxy = false; +}; + +# Open the p2p port in the firewall +networking.firewall.allowedTCPPorts = [ config.services.nix-bitcoin.port ]; +``` + +## Allow bitcoind RPC connections from LAN + +```nix +services.bitcoind = { + # Listen to connections on all interfaces + address = "0.0.0.0"; + + # Allow RPC connections from external addresses + rpc.allowip = [ + "10.10.0.0/24" # Allow a subnet + "10.50.0.3" # Allow a specific address + "0.0.0.0" # Allow all addresses + ]; + + # Set this if you're using the `secure-node.nix` template + tor.enforce = false; +}; + +# Open the RPC port in the firewall +networking.firewall.allowedTCPPorts = [ config.services.nix-bitcoin.rpc.port ]; +``` + +## Allow connections to electrs + +```nix +services.electrs = { + # Listen to connections on all interfaces + address = "0.0.0.0"; + + # Set this if you're using the `secure-node.nix` template + tor.enforce = false; +}; + +# Open the electrs port in the firewall +networking.firewall.allowedTCPPorts = [ config.services.electrs.port ]; +``` + +You can use the same approach to allow connections to other services. + +# Migrate existing services to nix-bitcoin + +## Example: bitcoind + +```shell +# 1. Stop bitcoind on your nodes +ssh root@nix-bitcoin-node 'systemctl stop bitcoind' +# Also stop bitcoind on the node that you'll be copying data from + +# 2. Copy the data to the nix-bitcoin node +# Important: Add a trailing slash to the source path +rsync /path/to/existing/bitcoind-datadir/ root@nix-bitcoin-node:/var/lib/bitcoind + +# 3. Fix data dir permissions on the nix-bitcoin node +ssh root@nix-bitcoin-node 'chown -R bitcoin: /var/lib/bitcoind' + +# 4. Start bitcoind +ssh root@nix-bitcoin-node 'systemctl start bitcoind' +``` + +You can use the same workflow for other services.\ +The default data dir path is `/var/lib/` for all services. + +Some services require extra steps: + +- lnd + + Copy your wallet password to `$secretsDir/lnd-wallet-password` (See: [Secrets dir](#secrets-dir)). + +- btcpayserver + + Copy the postgresql database: + ```shell + # Export (on the other node) + sudo -u postgres pg_dump YOUR_BTCPAYSERVER_DB > export.sql + # Restore (on the nix-bitcoin node) + sudo -u postgres psql btcpaydb < export.sql + ``` + +- joinmarket + + Copy your wallet to `/var/lib/joinmarket/wallets/wallet.jmdat`.\ + Write your wallet password, without a trailing newline, to + `$secretsDir/jm-wallet-password` (See: [Secrets dir](#secrets-dir)). + +# Use bitcoind from another node + +Use a bitcoind instance running on another node within a nix-bitcoin config. + +```nix +services.bitcoind = { + # Address of the other node + address = "10.10.0.2"; + rpc.users = let + # The fully privileged bitcoind RPC username of the other node + name = "myrpcuser"; + in { + privileged.name = name; + public.name = name; + ## Set this if you use btcpayserver + # btcpayserver.name = name; + ## Set this if you use joinmarket-ob-watcher + # joinmarket-ob-watcher.name = name; + }; +}; +# Disable the local bitcoind service +systemd.services.bitcoind.wantedBy = mkForce []; +``` + +Now save the password of the RPC user to the following files on your nix-bitcoin node: +```shell +$secretsDir/bitcoin-rpcpassword-privileged +$secretsDir/bitcoin-rpcpassword-public + +## Only needed when set in the above config snippet +# $secretsDir/bitcoin-rpcpassword-btcpayserver +# $secretsDir/bitcoin-rpcpassword-joinmarket-ob-watcher +``` +See: [Secrets dir](#secrets-dir) + +# Temporarily disable a service + +Sometimes you might want to disable a service without removing the service user and +integration with other services, as it would happen when setting +`services..enable = false`. + +Use the following approach: +``` +systemd.services..wantedBy = mkForce []; +``` +This way, the systemd service still exists, but is not automatically started. + +# Appendix + +## Secrets dir + +The secrets dir is set by option `nix-bitoin.secretsDir` and has the +following default values: + +- If you're using the krops deployment method: `/var/src/secrets` + +- Otherwise: + - `/secrets` (if you're using the `secure-node.nix` template) + - `/etc/nix-bitcoin-secrets` (otherwise) + + `/secrets` only exists to provide backwards compatibility for users of the + `secure-node.nix` template. diff --git a/docs/faq.md b/docs/faq.md index 7fa972e..53f7d1a 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,6 +1,19 @@ -* **Q:** The clightning service is running but when I try to use it (f.e. by running `lightning-cli getinfo` as user operator) all I get is `lightning-cli: Connecting to 'lightning-rpc': Connection refused`. - * **A:** Check your clightning logs with `journalctl -eu clightning`. Do you see something like `bitcoin-cli getblock ... false` failed? Are you using pruned mode? That means that clightning hasn't seen all the blocks it needs to and it can't get that block because your node is pruned. If you're just setting up a new node you can `systemctl stop clightning` and wipe your `/var/lib/clightning` directory. Otherwise you need to reindex the Bitcoin node. -* **Q:** My disk space is getting low due to nix. - * **A:** run `nix-collect-garbage -d` -* **Q:** Where is `sudo`??? - * **A:** we replaced sudo with OpenBSD's doas after [CVE-2021-3156](https://www.openwall.com/lists/oss-security/2021/01/26/3). It has greatly reduced complexity, and is therefore less likely to be a source of severe vulnerabilities in the future. +- **Q:** The clightning service is running but when I try to use it (f.e. by running + `lightning-cli getinfo` as user operator) all I get is `lightning-cli: Connecting + to 'lightning-rpc': Connection refused`.\ + **A:** Check your clightning logs with `journalctl -eu clightning`. Do you see + something like `bitcoin-cli getblock ... false` failed? Are you using pruned mode? + That means that clightning hasn't seen all the blocks it needs to and it can't get + that block because your node is pruned. \ + If you're just setting up a new node you can `systemctl stop clightning` and wipe + your `/var/lib/clightning` directory. Otherwise you need to reindex the Bitcoin + node. + +- **Q:** My disk space is getting low due to nix.\ + **A:** run `nix-collect-garbage -d` + +- **Q:** Where is `sudo`?\ + **A:** After [CVE-2021-3156](https://www.openwall.com/lists/oss-security/2021/01/26/3), + we've replaced `sudo` with OpenBSD's `doas` for users of the `secure-node.nix` template. + It has greatly reduced complexity and is therefore less likely to be a source of + severe vulnerabilities in the future. diff --git a/docs/install.md b/docs/install.md index b3c7fac..4aa2ee8 100644 --- a/docs/install.md +++ b/docs/install.md @@ -329,4 +329,7 @@ You can also build Nix from source by following the instructions at https://nixo For security reasons, all normal system management tasks can and should be performed with the `operator` user. Logging in as `root` should be done as rarely as possible. -See [usage.md](usage.md) for usage instructions, such as how to update. +See also: +- [Migrating existing services to bitcoind](configuration.md#migrate-existing-services-to-nix-bitcoin) +- [Managing your deployment](configuration.md#managing-your-deployment) +- [Using services](services.md) diff --git a/docs/usage.md b/docs/services.md similarity index 93% rename from docs/usage.md rename to docs/services.md index bb9aa50..572bc09 100644 --- a/docs/usage.md +++ b/docs/services.md @@ -1,18 +1,32 @@ -Updating ---- -In your deployment directory, enter the nix shell with `nix-shell` and run the -following to update `nix-bitcoin-release.nix`: - -``` -update-nix-bitcoin -``` - -Nodeinfo ---- +# Nodeinfo Run `nodeinfo` to see onion addresses and local addresses for enabled services. -Connect to RTL ---- +# Managing services + +NixOS uses the [systemd](https://wiki.archlinux.org/title/systemd) service manager. + +Usage: +```shell +# Show service status +systemctl status bitcoind + +# Show the last 100 log messages +journalctl -u bitcoind -n 100 +# Show all log messages since the last system boot +journalctl -b -u bitcoind + +# These commands require root permissions +systemctl stop bitcoind +systemctl start bitcoind +systemctl restart bitcoind + +# Show the service definition +systemctl cat bitcoind +# Show all service parameters +systemctl show bitcoind +``` + +# Connect to RTL Normally you would connect to RTL via SSH tunneling with a command like this ``` @@ -29,8 +43,7 @@ Otherwise, you can access it via Tor Browser at `http://`. You can find the `` with command `nodeinfo`. The default password location is `/secrets/rtl-password`. -Connect to spark-wallet ---- +# Connect to spark-wallet ### 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) @@ -73,8 +86,7 @@ Connect to spark-wallet Done ``` -Connect to LND with Zeus ---- +# Connect to LND with Zeus ### Requirements * Android phone * [Orbot](https://guardianproject.info/apps/orbot/) installed from @@ -110,8 +122,7 @@ Connect to LND with Zeus 5. Scan the QR code with your Zeus wallet and start sending Satoshis privately -Connect to electrs ---- +# Connect to electrs ### Requirements Android * 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) @@ -158,8 +169,7 @@ Connect to electrs Network > Server: :t ``` -Connect to nix-bitcoin node through the SSH onion service ---- +# Connect to nix-bitcoin node through the SSH onion service 1. Get the SSH onion address (excluding the port suffix) ``` @@ -195,8 +205,7 @@ Connect to nix-bitcoin node through the SSH onion service 6. After deploying the new configuration, it will connect through the SSH tunnel you established in step iv. This also allows you to do more complex SSH setups that some deployment tools don't support. An example would be authenticating with [Trezor's SSH agent](https://github.com/romanz/trezor-agent), which provides extra security. -Initialize a Trezor for Bitcoin Core's Hardware Wallet Interface ---- +# Initialize a Trezor for Bitcoin Core's Hardware Wallet Interface 1. Enable Trezor in `configuration.nix` @@ -258,8 +267,7 @@ Initialize a Trezor for Bitcoin Core's Hardware Wallet Interface 8. Follow Bitcoin Core's instructions on [Using Bitcoin Core with Hardware Wallets](https://github.com/bitcoin-core/HWI/blob/master/docs/bitcoin-core-usage.md) to use your Trezor with `bitcoin-cli` on your nix-bitcoin node -JoinMarket ---- +# JoinMarket ## Diff to regular JoinMarket usage @@ -380,8 +388,7 @@ See [here](https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/master 3. Profit -clightning ---- +# clightning ## Plugins diff --git a/examples/configuration.nix b/examples/configuration.nix index 03dd1ba..4e75f64 100644 --- a/examples/configuration.nix +++ b/examples/configuration.nix @@ -16,7 +16,7 @@ # See the comments at the top of `hardened-extended.nix` for further details. # - # FIXME: Uncomment next line to import your hardware configuration. If so, + # FIXME: Uncomment the next line to import your hardware configuration. If so, # add the hardware configuration file to the same directory as this file. #./hardware-configuration.nix ]; @@ -145,22 +145,6 @@ # # Liquid can be controlled with command 'elements-cli'. - ### RECURRING-DONATIONS - # Set this to enable recurring donations. This is EXPERIMENTAL; it's - # not guaranteed that payments are succeeding or that you will notice payment - # failure. - # services.recurring-donations.enable = true; - # This automatically enables clightning. - # - # Specify the receivers of the donations. By default donations are every - # Monday at a randomized time. Check `journalctl -eu recurring-donations` or - # `lightning-cli listpayments` for successful lightning donations. - # services.recurring-donations.tallycoin = { - # "" = " - # "" = ; - # "djbooth007" = 1000; - # }; - ### Hardware wallets # Enable the following to allow using hardware wallets. # See https://github.com/bitcoin-core/HWI for more information. diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index c7cd1ae..b38cf25 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -157,7 +157,7 @@ let }; proxy = mkOption { type = types.nullOr types.str; - default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null; + default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null; description = "Connect through SOCKS5 proxy"; }; i2p = mkOption { @@ -206,6 +206,11 @@ let Value 0 disables pruning. ''; }; + txindex = mkOption { + type = types.bool; + default = false; + description = "Enable the transaction index."; + }; zmqpubrawblock = mkOption { type = types.nullOr types.str; default = null; @@ -262,7 +267,7 @@ let ''; description = "Binary to connect with the bitcoind instance."; }; - enforceTor = nbLib.enforceTor; + tor = nbLib.tor; }; }; @@ -284,6 +289,7 @@ let ''} ${optionalString (cfg.dbCache != null) "dbcache=${toString cfg.dbCache}"} prune=${toString cfg.prune} + ${optionalString cfg.txindex "txindex=1"} ${optionalString (cfg.sysperms != null) "sysperms=${if cfg.sysperms then "1" else "0"}"} ${optionalString (cfg.disablewallet != null) "disablewallet=${if cfg.disablewallet then "1" else "0"}"} ${optionalString (cfg.assumevalid != null) "assumevalid=${cfg.assumevalid}"} @@ -407,7 +413,7 @@ in { Restart = "on-failure"; UMask = mkIf cfg.dataDirReadableByGroup "0027"; ReadWritePaths = cfg.dataDir; - } // nbLib.allowedIPAddresses cfg.enforceTor + } // nbLib.allowedIPAddresses cfg.tor.enforce // optionalAttrs zmqServerEnabled nbLib.allowNetlink; }; diff --git a/modules/btcpayserver.nix b/modules/btcpayserver.nix index 5143b48..94f6074 100644 --- a/modules/btcpayserver.nix +++ b/modules/btcpayserver.nix @@ -3,45 +3,6 @@ with lib; let options.services = { - nbxplorer = { - package = mkOption { - type = types.package; - default = nbPkgs.nbxplorer; - description = "The package providing nbxplorer binaries."; - }; - address = mkOption { - type = types.str; - default = "127.0.0.1"; - description = "Address to listen on."; - }; - port = mkOption { - type = types.port; - default = 24444; - description = "Port to listen on."; - }; - dataDir = mkOption { - type = types.path; - default = "/var/lib/nbxplorer"; - description = "The data directory for nbxplorer."; - }; - user = mkOption { - type = types.str; - default = "nbxplorer"; - description = "The user as which to run nbxplorer."; - }; - group = mkOption { - type = types.str; - default = cfg.nbxplorer.user; - description = "The group as which to run nbxplorer."; - }; - enable = mkOption { - # This option is only used by netns-isolation - internal = true; - default = cfg.btcpayserver.enable; - }; - enforceTor = nbLib.enforceTor; - }; - btcpayserver = { enable = mkEnableOption "btcpayserver"; address = mkOption { @@ -93,7 +54,49 @@ let default = cfg.btcpayserver.user; description = "The group as which to run btcpayserver."; }; - enforceTor = nbLib.enforceTor; + tor.enforce = nbLib.tor.enforce; + }; + + nbxplorer = { + enable = mkOption { + # This option is only used by netns-isolation + internal = true; + default = cfg.btcpayserver.enable; + description = '' + nbxplorer is always enabled when btcpayserver is enabled. + ''; + }; + package = mkOption { + type = types.package; + default = nbPkgs.nbxplorer; + description = "The package providing nbxplorer binaries."; + }; + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = "Address to listen on."; + }; + port = mkOption { + type = types.port; + default = 24444; + description = "Port to listen on."; + }; + dataDir = mkOption { + type = types.path; + default = "/var/lib/nbxplorer"; + description = "The data directory for nbxplorer."; + }; + user = mkOption { + type = types.str; + default = "nbxplorer"; + description = "The user as which to run nbxplorer."; + }; + group = mkOption { + type = types.str; + default = cfg.nbxplorer.user; + description = "The group as which to run nbxplorer."; + }; + tor.enforce = nbLib.tor.enforce; }; }; @@ -182,11 +185,11 @@ in { RestartSec = "10s"; ReadWritePaths = cfg.nbxplorer.dataDir; MemoryDenyWriteExecute = "false"; - } // nbLib.allowedIPAddresses cfg.nbxplorer.enforceTor; + } // nbLib.allowedIPAddresses cfg.nbxplorer.tor.enforce; }; systemd.services.btcpayserver = let - nbExplorerUrl = "http://${cfg.nbxplorer.address}:${toString cfg.nbxplorer.port}/"; + nbExplorerUrl = "http://${nbLib.addressWithPort cfg.nbxplorer.address cfg.nbxplorer.port}/"; nbExplorerCookie = "${cfg.nbxplorer.dataDir}/${bitcoind.makeNetworkName "Main" "RegTest"}/.cookie"; configFile = builtins.toFile "config" ('' network=${bitcoind.network} @@ -196,7 +199,8 @@ in { btcexplorerurl=${nbExplorerUrl} btcexplorercookiefile=${nbExplorerCookie} postgres=User ID=${cfg.btcpayserver.user};Host=/run/postgresql;Database=btcpaydb - ${optionalString (cfg.btcpayserver.rootpath != null) "rootpath=${cfg.btcpayserver.rootpath}"} + '' + optionalString (cfg.btcpayserver.rootpath != null) '' + rootpath=${cfg.btcpayserver.rootpath} '' + optionalString (cfg.btcpayserver.lightningBackend == "clightning") '' btclightning=type=clightning;server=unix:///${cfg.clightning.dataDir}/bitcoin/lightning-rpc '' + optionalString cfg.btcpayserver.lbtc '' @@ -234,7 +238,7 @@ in { RestartSec = "10s"; ReadWritePaths = cfg.btcpayserver.dataDir; MemoryDenyWriteExecute = "false"; - } // nbLib.allowedIPAddresses cfg.btcpayserver.enforceTor; + } // nbLib.allowedIPAddresses cfg.btcpayserver.tor.enforce; }; in self; users.users.${cfg.nbxplorer.user} = { diff --git a/modules/clightning-plugins/clboss.nix b/modules/clightning-plugins/clboss.nix index c50c85e..fe48358 100644 --- a/modules/clightning-plugins/clboss.nix +++ b/modules/clightning-plugins/clboss.nix @@ -9,7 +9,9 @@ let cfg = config.services.clightning.plugins.clboss; in type = types.ints.positive; default = 30000; description = '' - Specify target amount (in satoshi) that CLBOSS will leave onchain. + Target amount (in satoshi) that CLBOSS will leave on-chain. + clboss will only open new channels if this amount is smaller than + the funds in your clightning wallet. ''; }; package = mkOption { @@ -26,6 +28,6 @@ let cfg = config.services.clightning.plugins.clboss; in ''; systemd.services.clightning.path = [ pkgs.dnsutils - ] ++ optional config.services.clightning.enforceTor (hiPrio config.nix-bitcoin.torify); + ] ++ optional config.services.clightning.tor.proxy (hiPrio config.nix-bitcoin.torify); }; } diff --git a/modules/clightning.nix b/modules/clightning.nix index 44809e4..1c0cabf 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -16,14 +16,14 @@ let }; proxy = mkOption { type = types.nullOr types.str; - default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null; + default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null; 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; + default = cfg.tor.proxy; description = '' Always use the proxy, even to connect to normal IP addresses. You can still connect to Unix domain sockets manually. @@ -43,7 +43,16 @@ let extraConfig = mkOption { type = types.lines; default = ""; - description = "Extra lines appended to the configuration file."; + example = '' + alias=mynode + ''; + description = '' + Extra lines appended to the configuration file. + + See all available options at + https://github.com/ElementsProject/lightning/blob/master/doc/lightningd-config.5.md + or by running `lightningd --help`. + ''; }; user = mkOption { type = types.str; @@ -70,7 +79,7 @@ let If left empty, no address is announced. ''; }; - inherit (nbLib) enforceTor; + tor = nbLib.tor; }; cfg = config.services.clightning; @@ -88,6 +97,7 @@ let bitcoin-rpcport=${toString config.services.bitcoind.rpc.port} bitcoin-rpcuser=${config.services.bitcoind.rpc.users.public.name} rpc-file-mode=0660 + log-timestamps=false ${cfg.extraConfig} ''; @@ -123,13 +133,14 @@ in { preStart = '' # The RPC socket has to be removed otherwise we might have stale sockets rm -f ${cfg.networkDir}/lightning-rpc - install -m 640 ${configFile} '${cfg.dataDir}/config' + umask u=rw,g=r,o= { + cat ${configFile} echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-public)" ${optionalString (cfg.getPublicAddressCmd != "") '' echo "announce-addr=$(${cfg.getPublicAddressCmd}):${toString publicPort}" ''} - } >> '${cfg.dataDir}/config' + } > '${cfg.dataDir}/config' ''; serviceConfig = nbLib.defaultHardening // { ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; @@ -145,7 +156,7 @@ in { # # Disable seccomp filtering because clightning depends on this syscall. SystemCallFilter = []; - } // nbLib.allowedIPAddresses cfg.enforceTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; # Wait until the rpc socket appears postStart = '' while [[ ! -e ${cfg.networkDir}/lightning-rpc ]]; do diff --git a/modules/default.nix b/modules/default.nix index 6cef1ed..f173f9a 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -8,7 +8,6 @@ presets.secure-node = ./presets/secure-node.nix; rtl = ./rtl.nix; spark-wallet = ./spark-wallet.nix; - recurring-donations = ./recurring-donations.nix; lnd = ./lnd.nix; charge-lnd = ./charge-lnd.nix; joinmarket = ./joinmarket.nix; diff --git a/modules/electrs.nix b/modules/electrs.nix index 5e056e0..3091da1 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -12,7 +12,7 @@ let port = mkOption { type = types.port; default = 50001; - description = "RPC port."; + description = "Port to listen for RPC connections."; }; dataDir = mkOption { type = types.path; @@ -39,7 +39,7 @@ let default = cfg.user; description = "The group as which to run electrs."; }; - enforceTor = nbLib.enforceTor; + tor.enforce = nbLib.tor.enforce; }; cfg = config.services.electrs; @@ -74,10 +74,8 @@ in { > electrs.toml ''; serviceConfig = nbLib.defaultHardening // { - RuntimeDirectory = "electrs"; - RuntimeDirectoryMode = "700"; # electrs only uses the working directory for reading electrs.toml - WorkingDirectory = "/run/electrs"; + WorkingDirectory = cfg.dataDir; ExecStart = '' ${config.nix-bitcoin.pkgs.electrs}/bin/electrs \ --log-filters=INFO \ @@ -95,7 +93,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = cfg.dataDir; - } // nbLib.allowedIPAddresses cfg.enforceTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; }; users.users.${cfg.user} = { diff --git a/modules/joinmarket-ob-watcher.nix b/modules/joinmarket-ob-watcher.nix index 7353798..27b0b2c 100644 --- a/modules/joinmarket-ob-watcher.nix +++ b/modules/joinmarket-ob-watcher.nix @@ -29,11 +29,9 @@ let default = cfg.user; description = "The group as which to run JoinMarket."; }; - # This option is only used by netns-isolation - enforceTor = mkOption { - readOnly = true; - default = true; - }; + # This option is only used by netns-isolation. + # Tor is always enabled. + tor.enforce = nbLib.tor.enforce; }; cfg = config.services.joinmarket-ob-watcher; @@ -100,7 +98,7 @@ in { SystemCallFilter = nbLib.defaultHardening.SystemCallFilter ++ [ "mbind" ] ; Restart = "on-failure"; RestartSec = "10s"; - } // nbLib.allowTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; }; users.users.${cfg.user} = { diff --git a/modules/joinmarket.nix b/modules/joinmarket.nix index 58ca77a..94a3479 100644 --- a/modules/joinmarket.nix +++ b/modules/joinmarket.nix @@ -50,11 +50,9 @@ let readOnly = true; default = ircServers; }; - # This option is only used by netns-isolation - enforceTor = mkOption { - readOnly = true; - default = true; - }; + # This option is only used by netns-isolation. + # Tor is always enabled. + tor.enforce = nbLib.tor.enforce; inherit (nbLib) cliExec; yieldgenerator = { @@ -328,7 +326,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = cfg.dataDir; - } // nbLib.allowTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; }; users.users.${cfg.user} = { diff --git a/modules/lightning-loop.nix b/modules/lightning-loop.nix index 1249a3a..2d2441a 100644 --- a/modules/lightning-loop.nix +++ b/modules/lightning-loop.nix @@ -36,7 +36,7 @@ let }; proxy = mkOption { type = types.nullOr types.str; - default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null; + default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null; description = "host:port of SOCKS5 proxy for connnecting to the loop server."; }; extraConfig = mkOption { @@ -56,7 +56,7 @@ let ''; description = "Binary to connect with the lightning-loop instance."; }; - enforceTor = nbLib.enforceTor; + tor = nbLib.tor; }; cfg = config.services.lightning-loop; @@ -105,7 +105,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = cfg.dataDir; - } // nbLib.allowedIPAddresses cfg.enforceTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; }; nix-bitcoin.secrets = { diff --git a/modules/lightning-pool.nix b/modules/lightning-pool.nix index 0c69d32..c93aa6a 100644 --- a/modules/lightning-pool.nix +++ b/modules/lightning-pool.nix @@ -36,7 +36,7 @@ let }; proxy = mkOption { type = types.nullOr types.str; - default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null; + default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null; description = "host:port of SOCKS5 proxy for connnecting to the pool auction server."; }; extraConfig = mkOption { @@ -56,7 +56,7 @@ let ''; description = "Binary to connect with the lightning-pool instance."; }; - enforceTor = nbLib.enforceTor; + tor = nbLib.tor; }; cfg = config.services.lightning-pool; @@ -102,7 +102,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = cfg.dataDir; - } // (nbLib.allowedIPAddresses cfg.enforceTor) + } // (nbLib.allowedIPAddresses cfg.tor.enforce) // nbLib.allowNetlink; # required by gRPC-Go }; }; diff --git a/modules/liquid.nix b/modules/liquid.nix index dd8d884..6e7e6ad 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -98,7 +98,7 @@ let }; proxy = mkOption { type = types.nullOr types.str; - default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null; + default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null; description = "Connect through SOCKS5 proxy"; }; dbCache = mkOption { @@ -156,7 +156,7 @@ let ''; description = "Binary for managing liquid swaps."; }; - enforceTor = nbLib.enforceTor; + tor = nbLib.tor; }; }; @@ -271,7 +271,7 @@ in { ExecStart = "${nbPkgs.elementsd}/bin/elementsd -datadir='${cfg.dataDir}'"; Restart = "on-failure"; ReadWritePaths = cfg.dataDir; - } // nbLib.allowedIPAddresses cfg.enforceTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; }; users.users.${cfg.user} = { diff --git a/modules/lnd.nix b/modules/lnd.nix index 0e1c76a..5d14846 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -46,7 +46,7 @@ let }; tor-socks = mkOption { type = types.nullOr types.str; - default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null; + default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null; description = "Socks proxy for connecting to Tor nodes"; }; macaroons = mkOption { @@ -117,7 +117,7 @@ let default = "${secretsDir}/lnd-cert"; description = "LND TLS certificate path."; }; - inherit (nbLib) enforceTor; + tor = nbLib.tor; }; cfg = config.services.lnd; @@ -143,7 +143,7 @@ let bitcoin.active=1 bitcoin.node=bitcoind - ${optionalString (cfg.enforceTor) "tor.active=true"} + ${optionalString (cfg.tor.proxy) "tor.active=true"} ${optionalString (cfg.tor-socks != null) "tor.socks=${cfg.tor-socks}"} bitcoind.rpchost=${bitcoindRpcAddress}:${toString bitcoind.rpc.port} @@ -277,7 +277,7 @@ in { '') (attrNames cfg.macaroons)} '') ]; - } // nbLib.allowedIPAddresses cfg.enforceTor; + } // nbLib.allowedIPAddresses cfg.tor.enforce; }; users.users.${cfg.user} = { diff --git a/modules/modules.nix b/modules/modules.nix index 428eff8..a0512f6 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -12,20 +12,19 @@ ./bitcoind.nix ./clightning.nix ./clightning-plugins - ./rtl.nix ./spark-wallet.nix ./lnd.nix ./lnd-rest-onion-service.nix # Requires onion-addresses.nix ./lightning-loop.nix ./lightning-pool.nix ./charge-lnd.nix + ./rtl.nix ./electrs.nix ./liquid.nix ./btcpayserver.nix ./joinmarket.nix ./joinmarket-ob-watcher.nix ./hardware-wallets.nix - ./recurring-donations.nix # Support features ./versioning.nix diff --git a/modules/netns-isolation.nix b/modules/netns-isolation.nix index ff39ac8..471716a 100644 --- a/modules/netns-isolation.nix +++ b/modules/netns-isolation.nix @@ -70,7 +70,8 @@ let # and # availableNetns.clighting = [ "bitcoind" ]; # - # FIXME: Although negligible for our purposes, this calculation's runtime + # TODO-EXTERNAL: + # Although negligible for our purposes, this calculation's runtime # is in the order of (number of connections * number of services), # because attrsets and lists are fully copied on each update with '//' or '++'. # This can only be improved with an update in the nix language. @@ -156,7 +157,9 @@ in { peer = "nb-veth-br-${toString v.id}"; inherit (v) netnsName; nsenter = "${pkgs.utillinux}/bin/nsenter"; - allowedAddresses = concatMapStringsSep "," (available: netns.${available}.address) v.availableNetns; + allowedNetnsAddresses = map (available: netns.${available}.address) v.availableNetns; + allowedAddresses = concatStringsSep "," + ([ "127.0.0.1,${bridgeIp},${v.address}" ] ++ allowedNetnsAddresses); setup = '' ${ip} netns add ${netnsName} @@ -176,17 +179,13 @@ in { ${ip} route add default via ${bridgeIp} ${iptables} -w -P INPUT DROP - ${iptables} -w -A INPUT -s 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT # allow return traffic to outgoing connections initiated by the service itself ${iptables} -w -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT - '' + optionalString (config.services.${n}.enforceTor or false) '' - ${iptables} -w -P OUTPUT DROP - ${iptables} -w -A OUTPUT -d 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT - '' + optionalString (v.availableNetns != []) '' ${iptables} -w -A INPUT -s ${allowedAddresses} -j ACCEPT + '' + optionalString (config.services.${n}.tor.enforce or false) '' + ${iptables} -w -P OUTPUT DROP ${iptables} -w -A OUTPUT -d ${allowedAddresses} -j ACCEPT ''; - script = name: src: pkgs.writers.writeDash name '' set -e ${src} @@ -246,10 +245,6 @@ in { id = 17; # communicates with clightning over lightning-rpc socket }; - recurring-donations = { - id = 20; - # communicates with clightning over lightning-rpc socket - }; nginx = { id = 21; }; @@ -336,14 +331,16 @@ in { payjoinAddress = netns.joinmarket.address; cliExec = mkCliExec "joinmarket"; }; - systemd.services.joinmarket-yieldgenerator.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket"; + systemd.services.joinmarket-yieldgenerator = mkIf config.services.joinmarket.yieldgenerator.enable { + serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket"; + }; services.joinmarket-ob-watcher.address = netns.joinmarket-ob-watcher.address; services.lightning-pool.rpcAddress = netns.lightning-pool.address; services.rtl.address = netns.rtl.address; - systemd.services.cl-rest = { + systemd.services.cl-rest = mkIf config.services.rtl.cl-rest.enable { serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-rtl"; requires = [ "netns-rtl.service" ] ; after = [ "netns-rtl.service" ]; diff --git a/modules/nix-bitcoin.nix b/modules/nix-bitcoin.nix index bad87c1..605c115 100644 --- a/modules/nix-bitcoin.nix +++ b/modules/nix-bitcoin.nix @@ -35,7 +35,7 @@ with lib; runAsUserCmd = mkOption { readOnly = true; default = if config.security.doas.enable - # TODO: Use absolute path until https://github.com/NixOS/nixpkgs/pull/133622 is available. + # TODO-EXTERNAL: Use absolute path until https://github.com/NixOS/nixpkgs/pull/133622 is available. then "/run/wrappers/bin/doas -u" else "sudo -u"; }; diff --git a/modules/obsolete-options.nix b/modules/obsolete-options.nix index 137165f..fc79164 100644 --- a/modules/obsolete-options.nix +++ b/modules/obsolete-options.nix @@ -7,6 +7,16 @@ let mkRemovedOptionModule [ "services" service "announce-tor" ] '' Use option `nix-bitcoin.onionServices.${service}.public` instead. ''; + + mkSplitEnforceTorOption = service: + (mkRemovedOptionModule [ "services" service "enforceTor" ] '' + The option has been split into options `tor.proxy` and `tor.enforce`. + Set `tor.proxy = true` to proxy outgoing connections with Tor. + Set `tor.enforce = true` to only allow connections (incoming and outgoing) through Tor. + ''); + mkRenamedEnforceTorOption = service: + (mkRenamedOptionModule [ "services" service "enforceTor" ] [ "services" service "tor" "enforce" ]); + in { imports = [ (mkRenamedOptionModule [ "services" "bitcoind" "bind" ] [ "services" "bitcoind" "address" ]) @@ -33,5 +43,20 @@ in { bitcoin peer connections for syncing blocks. This performs well on low and high memory systems. '') - ]; + ] ++ + # 0.0.59 + (map mkSplitEnforceTorOption [ + "clightning" + "lightning-loop" + "lightning-pool" + "liquid" + "lnd" + "spark-wallet" + "bitcoind" + ]) ++ + (map mkRenamedEnforceTorOption [ + "btcpayserver" + "rtl" + "electrs" + ]); } diff --git a/modules/onion-addresses.nix b/modules/onion-addresses.nix index 9a5cd47..07466c5 100644 --- a/modules/onion-addresses.nix +++ b/modules/onion-addresses.nix @@ -58,8 +58,20 @@ in { CapabilityBoundingSet = "CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER"; }; script = '' + waitForFile() { + file=$1 + for ((i=0; i<300; i++)); do + if [[ -e $file ]]; then + return; + fi + sleep 0.1 + done + echo "Error: File $file did not appear after 30 sec." + exit 1 + } + # Wait until tor is up - until [[ -e /var/lib/tor/state ]]; do sleep 0.1; done + waitForFile /var/lib/tor/state cd ${cfg.dataDir} rm -rf * @@ -71,22 +83,20 @@ in { ${concatMapStrings (service: '' onionFile=/var/lib/tor/onion/${service}/hostname - if [[ -e $onionFile ]]; then - cp $onionFile ${user}/${service} - chown ${user} ${user}/${service} - fi + waitForFile $onionFile + cp $onionFile ${user}/${service} + chown ${user} ${user}/${service} '') cfg.access.${user} - } + } '') (builtins.attrNames cfg.access) } ${concatMapStrings (service: '' onionFile=/var/lib/tor/onion/${service}/hostname - if [[ -e $onionFile ]]; then - install -D -o ${config.systemd.services.${service}.serviceConfig.User} -m 400 $onionFile services/${service} - fi + waitForFile $onionFile + install -D -o ${config.systemd.services.${service}.serviceConfig.User} -m 400 $onionFile services/${service} '') cfg.services} ''; }; diff --git a/modules/presets/enable-tor.nix b/modules/presets/enable-tor.nix index 583c6c1..a63634c 100644 --- a/modules/presets/enable-tor.nix +++ b/modules/presets/enable-tor.nix @@ -1,27 +1,43 @@ { lib, config, ... }: let defaultTrue = lib.mkDefault true; + defaultEnableTorProxy = { + tor.proxy = defaultTrue; + tor.enforce = defaultTrue; + }; + defaultEnforceTor = { + tor.enforce = defaultTrue; + }; in { services.tor = { enable = true; client.enable = true; }; - # Use Tor for all outgoing connections services = { - bitcoind.enforceTor = true; - clightning.enforceTor = true; - lnd.enforceTor = true; - lightning-loop.enforceTor = true; - liquidd.enforceTor = true; - electrs.enforceTor = true; + # Use Tor as a proxy for outgoing connections + # and restrict all connections to Tor + # + bitcoind = defaultEnableTorProxy; + clightning = defaultEnableTorProxy; + lnd = defaultEnableTorProxy; + lightning-loop = defaultEnableTorProxy; + liquidd = defaultEnableTorProxy; + # TODO-EXTERNAL: # disable Tor enforcement until btcpayserver can fetch rates over Tor - # btcpayserver.enforceTor = true; - nbxplorer.enforceTor = true; - spark-wallet.enforceTor = true; - recurring-donations.enforceTor = true; - lightning-pool.enforceTor = true; - rtl.enforceTor = true; + # btcpayserver = defaultEnableTorProxy; + spark-wallet = defaultEnableTorProxy; + lightning-pool = defaultEnableTorProxy; + + # These services don't make outgoing connections + # (or use Tor by default in case of joinmarket) + # but we restrict them to Tor just to be safe. + # + electrs = defaultEnforceTor; + nbxplorer = defaultEnforceTor; + rtl = defaultEnforceTor; + joinmarket = defaultEnforceTor; + joinmarket-ob-watcher = defaultEnforceTor; }; # Add onion services for incoming connections diff --git a/modules/recurring-donations.nix b/modules/recurring-donations.nix deleted file mode 100644 index 84922a0..0000000 --- a/modules/recurring-donations.nix +++ /dev/null @@ -1,107 +0,0 @@ -{ config, lib, pkgs, ... }: - -with lib; -let - options.services.recurring-donations = { - enable = mkEnableOption "recurring-donations"; - tallycoin = mkOption { - type = types.attrs; - default = {}; - description = '' - This option is used to specify tallycoin donation receivers using an - attribute set. For example the following setting instructs the module - to repeatedly send 1000 satoshis to djbooth007. - { - "djbooth007" = 1000; - } - ''; - }; - interval = mkOption { - type = types.str; - default = "Mon *-*-* 00:00:00"; - description = '' - Schedules the donations. Default is weekly on Mon 00:00:00. See `man - systemd.time` for further options. - ''; - }; - randomizedDelaySec = mkOption { - type = types.int; - default = 86400; - description = '' - Random delay to add to scheduled time for donation. Default is one day. - ''; - }; - enforceTor = nbLib.enforceTor; - }; - - cfg = config.services.recurring-donations; - 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() { - NAME=$1 - AMOUNT=$2 - echo "Attempting to pay $AMOUNT sat to $NAME" - INVOICE=$(curl --socks5-hostname ${config.nix-bitcoin.torClientAddressWithPort} -d "satoshi_amount=$AMOUNT&payment_method=ln&id=$NAME&type=profile" -X POST https://api.tallyco.in/v1/payment/request/ | jq -r '.lightning_pay_request') 2> /dev/null - if [ -z "$INVOICE" ] || [ "$INVOICE" = "null" ]; then - echo "ERROR: did not get invoice from tallycoin" - return - fi - # Decode invoice and compare amount with requested amount - DECODED_AMOUNT=$($LNCLI decodepay "$INVOICE" | jq -r '.amount_msat' | head -c -8) - if [ -z "$DECODED_AMOUNT" ] || [ "$DECODED_AMOUNT" = "null" ]; then - echo "ERROR: did not get response from clightning" - return - fi - if [ $DECODED_AMOUNT -eq $AMOUNT ]; then - echo "Paying with invoice $INVOICE" - $LNCLI pay "$INVOICE" - else - echo "ERROR: requested amount and invoice amount do not match. $AMOUNT vs $DECODED_AMOUNT" - return - fi - } - ${ builtins.foldl' - (x: receiver: x + - '' - pay_tallycoin ${receiver} ${toString (builtins.getAttr receiver cfg.tallycoin)} - '') - "" - (builtins.attrNames cfg.tallycoin) - } - ''; -in { - inherit options; - - config = mkIf cfg.enable { - services.clightning.enable = true; - - systemd.services.recurring-donations = { - requires = [ "clightning.service" ]; - after = [ "clightning.service" ]; - path = with pkgs; [ nix-bitcoin.clightning curl jq ]; - serviceConfig = nbLib.defaultHardening // { - ExecStart = "${pkgs.bash}/bin/bash ${recurring-donations-script}"; - User = "recurring-donations"; - Type = "oneshot"; - } // nbLib.allowedIPAddresses cfg.enforceTor; - }; - systemd.timers.recurring-donations = { - requires = [ "clightning.service" ]; - after = [ "clightning.service" ]; - timerConfig = { - Unit = "recurring-donations.service"; - OnCalendar = cfg.interval; - RandomizedDelaySec = toString cfg.randomizedDelaySec; - }; - wantedBy = [ "multi-user.target" ]; - }; - - users.users.recurring-donations = { - isSystemUser = true; - group = "recurring-donations"; - extraGroups = [ config.services.clightning.group ]; - }; - users.groups.recurring-donations = {}; - }; -} diff --git a/modules/rtl.nix b/modules/rtl.nix index 70797f1..095fe4a 100644 --- a/modules/rtl.nix +++ b/modules/rtl.nix @@ -23,12 +23,12 @@ let clightning = mkOption { type = types.bool; default = false; - description = "Add a node interface for clightning."; + description = "Enable the clightning node interface."; }; lnd = mkOption { type = types.bool; default = false; - description = "Add a node interface for lnd."; + description = "Enable the lnd node interface."; }; reverseOrder = mkOption { type = types.bool; @@ -49,11 +49,36 @@ let default = false; description = "Enable the Night UI Theme."; }; + extraCurrency = mkOption { + type = with types; nullOr str; + default = null; + example = "USD"; + description = '' + Currency code (ISO 4217) of the extra currency used for displaying balances. + When set, this option enables online currency rate fetching. + Warning: Rate fetching requires outgoing clearnet connections, so option + `tor.enforce` is automatically disabled. + ''; + }; + user = mkOption { + type = types.str; + default = "rtl"; + description = "The user as which to run RTL."; + }; + group = mkOption { + type = types.str; + default = cfg.user; + description = "The group as which to run RTL."; + }; cl-rest = { enable = mkOption { + readOnly = true; type = types.bool; default = cfg.nodes.clightning; - description = "Enable c-lightning-REST server."; + description = '' + Enable c-lightning-REST server. This service is required for + clightning support and is automatically enabled. + ''; }; address = mkOption { readOnly = true; @@ -75,17 +100,7 @@ let description = "Swagger API documentation server port."; }; }; - user = mkOption { - type = types.str; - default = "rtl"; - description = "The user as which to run RTL."; - }; - group = mkOption { - type = types.str; - default = cfg.user; - description = "The group as which to run RTL."; - }; - inherit (nbLib) enforceTor; + tor.enforce = nbLib.tor.enforce; }; cfg = config.services.rtl; @@ -114,7 +129,10 @@ let ''"channelBackupPath": "${cfg.dataDir}/backup/lnd",'' } "logLevel": "INFO", - "fiatConversion": false, + "fiatConversion": ${if cfg.extraCurrency == null then "false" else "true"}, + ${optionalString (cfg.extraCurrency != null) + ''"currencyUnit": "${cfg.extraCurrency}",'' + } ${optionalString (isLnd && cfg.loop) ''"swapServerUrl": "https://${nbLib.addressWithPort lightning-loop.restAddress lightning-loop.restPort}",'' } @@ -186,6 +204,8 @@ in { "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" ]; + services.rtl.tor.enforce = mkIf (cfg.extraCurrency != null) false; + systemd.services.rtl = rec { wantedBy = [ "multi-user.target" ]; requires = optional cfg.nodes.clightning "cl-rest.service" ++ @@ -210,7 +230,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = cfg.dataDir; - } // nbLib.allowedIPAddresses cfg.enforceTor + } // nbLib.allowedIPAddresses cfg.tor.enforce // nbLib.nodejs; }; diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index a55be9a..adf6c87 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -38,7 +38,7 @@ let default = cfg.user; description = "The group as which to run spark-wallet."; }; - inherit (nbLib) enforceTor; + tor = nbLib.tor; }; cfg = config.services.spark-wallet; @@ -57,7 +57,7 @@ let --ln-path '${clightning.networkDir}' \ --host ${cfg.address} --port ${toString cfg.port} \ --config '${config.nix-bitcoin.secretsDir}/spark-wallet-login' \ - ${optionalString cfg.enforceTor torRateProvider} \ + ${optionalString cfg.tor.proxy torRateProvider} \ $publicURL \ --pairing-qr --print-key ${cfg.extraArgs} ''; @@ -76,7 +76,7 @@ in { User = cfg.user; Restart = "on-failure"; RestartSec = "10s"; - } // nbLib.allowedIPAddresses cfg.enforceTor + } // nbLib.allowedIPAddresses cfg.tor.enforce // nbLib.nodejs; }; diff --git a/pkgs/lib.nix b/pkgs/lib.nix index 61db1b0..9181796 100644 --- a/pkgs/lib.nix +++ b/pkgs/lib.nix @@ -55,13 +55,20 @@ let self = { then self.allowLocalIPAddresses else self.allowAllIPAddresses; - enforceTor = mkOption { - type = types.bool; - default = false; - description = '' - Whether to force Tor on a service by only allowing connections from and - to 127.0.0.1; - ''; + tor = { + proxy = mkOption { + type = types.bool; + default = false; + description = "Whether to proxy outgoing connections with Tor."; + }; + enforce = mkOption { + type = types.bool; + default = false; + description = '' + Whether to enforce Tor on a service by only allowing connections + from and to localhost and link-local addresses. + ''; + }; }; script = name: src: pkgs.writers.writeBash name '' diff --git a/pkgs/python-packages/pyln-bolt7/default.nix b/pkgs/python-packages/pyln-bolt7/default.nix index 8433ba0..84cb855 100644 --- a/pkgs/python-packages/pyln-bolt7/default.nix +++ b/pkgs/python-packages/pyln-bolt7/default.nix @@ -13,7 +13,7 @@ buildPythonPackage rec { postUnpack = "sourceRoot=$sourceRoot/contrib/pyln-spec/bolt7"; - # TODO-EXTERNAL + # TODO-EXTERNAL: # Remove when this fix is released # https://github.com/ElementsProject/lightning/pull/4910 postPatch = '' diff --git a/test/tests.nix b/test/tests.nix index 9afcd97..7f06a16 100644 --- a/test/tests.nix +++ b/test/tests.nix @@ -67,6 +67,7 @@ let nix-bitcoin.generateSecretsCmds.rtl = mkIf cfg.rtl.enable (mkForce '' echo a > rtl-password ''); + services.rtl.extraCurrency = mkDefault "CHF"; tests.spark-wallet = cfg.spark-wallet.enable; diff --git a/test/tests.py b/test/tests.py index af7cb81..d7224c4 100644 --- a/test/tests.py +++ b/test/tests.py @@ -375,7 +375,6 @@ def _(): num_blocks = test_data["num_blocks"] if enabled("electrs"): - machine.wait_for_unit("onion-addresses") machine.wait_until_succeeds(log_has_string("electrs", "serving Electrum RPC")) get_block_height_cmd = ( """echo '{"method": "blockchain.headers.subscribe", "id": 0, "params": []}'"""