diff --git a/README.md b/README.md
index 62d2970c..2f5d5f94 100644
--- a/README.md
+++ b/README.md
@@ -48,8 +48,7 @@ See the [examples directory](examples/README.md).
Features
---
A [configuration preset](modules/presets/secure-node.nix) for setting up a secure node
-* All applications use Tor for outbound connections and accept inbound connections via onion services.
-* Includes a [nodeinfo](modules/nodeinfo.nix) script which prints basic info about the node.
+* All applications use Tor for outbound connections and support accepting inbound connections via onion services.
NixOS modules
* Application services
@@ -74,9 +73,9 @@ NixOS modules
* [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
+ * [nodeinfo](modules/nodeinfo.nix): script which prints info about the node's services
* [backups](modules/backups.nix): daily duplicity backups of all your node's important files
* [operator](modules/operator.nix): adds non-root user `operator` who has access to client tools (e.g. `bitcoin-cli`, `lightning-cli`)
- * [nix-bitcoin webindex](modules/nix-bitcoin-webindex.nix): a local website to display node information
Security
---
diff --git a/docs/usage.md b/docs/usage.md
index 53d1c8b2..5d3985d4 100644
--- a/docs/usage.md
+++ b/docs/usage.md
@@ -8,7 +8,7 @@ fetch-release > nix-bitcoin-release.nix
Nodeinfo
---
-Run `nodeinfo` to see your onion addresses for the webindex, spark, etc. if they are enabled.
+Run `nodeinfo` to see onion addresses and local addresses for enabled services.
Connect to spark-wallet
---
@@ -86,10 +86,10 @@ Connect to electrs
nixops deploy -d bitcoin-node
```
-3. Get electrs onion address
+3. Get electrs onion address with format `:`
```
- nodeinfo | grep 'ELECTRS_ONION'
+ nodeinfo | jq -r .electrs.onion_address
```
4. Connect to electrs
@@ -98,7 +98,7 @@ Connect to electrs
On Desktop
```
- electrum --oneserver -1 -s ":50001:t" -p socks5:localhost:9050
+ electrum --oneserver -1 -s ":t" -p socks5:localhost:9050
```
On Android
@@ -107,16 +107,16 @@ Connect to electrs
Network > Proxy mode: socks5, Host: 127.0.0.1, Port: 9050
Network > Auto-connect: OFF
Network > One-server mode: ON
- Network > Server: :50001:t
+ Network > Server: :t
```
-Connect to nix-bitcoin node through ssh Tor Hidden Service
+Connect to nix-bitcoin node through the SSH onion service
---
-1. Run `nodeinfo` on your nix-bitcoin node and note the `SSHD_ONION`
+1. Get the SSH onion address (excluding the port suffix)
```
nixops ssh operator@bitcoin-node
- nodeinfo | grep 'SSHD_ONION'
+ nodeinfo | jq -r .sshd.onion_address | sed 's/:.*//'
```
2. Create a SSH key
@@ -131,14 +131,14 @@ Connect to nix-bitcoin node through ssh Tor Hidden Service
# FIXME: Add your SSH pubkey
services.openssh.enable = true;
users.users.root = {
- openssh.authorizedKeys.keys = [ "[contents of ~/.ssh/id_ed25519.pub]" ];
+ openssh.authorizedKeys.keys = [ "" ];
};
```
-4. Connect to your nix-bitcoin node's ssh Tor Hidden Service, forwarding a local port to the nix-bitcoin node's ssh server
+4. Connect to your nix-bitcoin node's SSH onion service, forwarding a local port to the nix-bitcoin node's SSH server
```
- ssh -i ~/.ssh/id_ed25519 -L [random port of your choosing]:localhost:22 root@[your SSHD_ONION]
+ ssh -i ~/.ssh/id_ed25519 -L :localhost:22 root@
```
5. Edit your `network-nixos.nix` to look like this
@@ -148,12 +148,12 @@ Connect to nix-bitcoin node through ssh Tor Hidden Service
bitcoin-node =
{ config, pkgs, ... }:
{ deployment.targetHost = "127.0.0.1";
- deployment.targetPort = [random port of your choosing];
+ deployment.targetPort = ;
};
}
```
-6. Now you can run `nixops deploy -d bitcoin-node` and it will connect through the ssh tunnel you established in step iv. This also allows you to do more complex ssh setups that `nixops ssh` doesn't support. An example would be authenticating with [Trezor's ssh agent](https://github.com/romanz/trezor-agent), which provides extra security.
+6. Now you can run `nixops deploy -d bitcoin-node` and it will connect through the SSH tunnel you established in step iv. This also allows you to do more complex SSH setups that `nixops ssh` doesn'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
---
@@ -263,7 +263,7 @@ you. If however, you want to manually initialize your wallet, follow these steps
## Run the tumbler
The tumbler needs to be able to run in the background for a long time, use screen
-to run it accross ssh sessions. You can also use tmux in the same fashion.
+to run it accross SSH sessions. You can also use tmux in the same fashion.
1. Add screen to your `environment.systemPackages`, for example
diff --git a/examples/README.md b/examples/README.md
index 1280afbb..328c6aac 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -11,7 +11,7 @@ nix-shell
The following example scripts set up a nix-bitcoin node according to [`configuration.nix`](configuration.nix) and then
shut down immediately. They leave no traces (outside of `/nix/store`) on the host system.\
-By default, [`configuration.nix`](configuration.nix) enables `bitcoind` and `clightning` (with an onion service).
+By default, [`configuration.nix`](configuration.nix) enables `bitcoind` and `clightning`.
- [`./deploy-container.sh`](deploy-container.sh) creates a [NixOS container](https://github.com/erikarvstedt/extra-container).\
This is the fastest way to set up a node.\
diff --git a/examples/configuration.nix b/examples/configuration.nix
index 9545d0fe..8ebd0370 100644
--- a/examples/configuration.nix
+++ b/examples/configuration.nix
@@ -37,11 +37,12 @@
# Enable this module to use clightning, a Lightning Network implementation
# in C.
services.clightning.enable = true;
- # == TOR
- # Enable this option to announce our Tor Hidden Service. By default clightning
- # offers outgoing functionality, but doesn't announce the Tor Hidden Service
- # under which peers can reach us.
- # services.clightning.announce-tor = true;
+ #
+ # Set this to create an onion service by which clightning can accept incoming connections
+ # via Tor.
+ # The onion service is automatically announced to peers.
+ # nix-bitcoin.onionServices.clightning.public = true;
+ #
# == Plugins
# See ../docs/usage.md for the list of available plugins.
# services.clightning.plugins.prometheus.enable = true;
@@ -49,13 +50,15 @@
### LND
# Uncomment the following line in order to enable lnd, a lightning
# implementation written in Go. In order to avoid collisions with clightning
- # you must disable clightning or change the services.clightning.bindport or
- # services.lnd.listenPort to a port other than 9735.
+ # you must disable clightning or change the services.clightning.port or
+ # services.lnd.port to a port other than 9735.
# services.lnd.enable = true;
- # Enable this option to announce our Tor Hidden Service. By default lnd
- # offers outgoing functionality, but doesn't announce the Tor Hidden Service
- # under which peers can reach us.
- # services.lnd.announce-tor = true;
+ #
+ # Set this to create an onion service by which lnd can accept incoming connections
+ # via Tor.
+ # The onion service is automatically announced to peers.
+ # nix-bitcoin.onionServices.lnd.public = 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
@@ -93,6 +96,12 @@
# The lightning backend service automatically enabled.
# Afterwards you need to go into Store > General Settings > Lightning Nodes
# and click to use "the internal lightning node of this BTCPay Server".
+ #
+ # Set this to create an onion service to make the btcpayserver web interface
+ # accessible via Tor.
+ # Security WARNING: Create a btcpayserver administrator account before allowing
+ # public access to the web interface.
+ # nix-bitcoin.onionServices.btcpayserver.enable = true;
### LIQUIDD
# Enable this module to use Liquid, a sidechain for an inter-exchange
@@ -101,11 +110,6 @@
# tool run as user operator.
# services.liquidd.enable = true;
- ### WEBINDEX
- # Enable this module to use the nix-bitcoin-webindex, a simple website
- # displaying your node information. Only available if clightning is enabled.
- # services.nix-bitcoin-webindex.enable = true;
-
### RECURRING-DONATIONS
# Enable this module to send recurring donations. This is EXPERIMENTAL; it's
# not guaranteed that payments are succeeding or that you will notice payment
@@ -203,5 +207,5 @@
# The nix-bitcoin release version that your config is compatible with.
# When upgrading to a backwards-incompatible release, nix-bitcoin will display an
# an error and provide hints for migrating your config to the new release.
- nix-bitcoin.configVersion = "0.0.26";
+ nix-bitcoin.configVersion = "0.0.30";
}
diff --git a/modules/backups.nix b/modules/backups.nix
index 7060980a..322a84c6 100644
--- a/modules/backups.nix
+++ b/modules/backups.nix
@@ -31,13 +31,6 @@ let
in {
options.services.backups = {
enable = mkEnableOption "Backups service";
- program = mkOption {
- type = types.enum [ "duplicity" ];
- default = "duplicity";
- description = ''
- Program with which to do backups.
- '';
- };
with-bulk-data = mkOption {
type = types.bool;
default = false;
@@ -69,7 +62,7 @@ in {
};
};
- config = mkIf (cfg.enable && cfg.program == "duplicity") (mkMerge [
+ config = mkIf cfg.enable (mkMerge [
{
environment.systemPackages = [ pkgs.duplicity ];
diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix
index c8650ebb..92f3516d 100644
--- a/modules/bitcoind.nix
+++ b/modules/bitcoind.nix
@@ -22,16 +22,18 @@ let
${optionalString (cfg.assumevalid != null) "assumevalid=${cfg.assumevalid}"}
# Connection options
- ${optionalString cfg.listen "bind=${cfg.bind}"}
- ${optionalString (cfg.port != null) "port=${toString cfg.port}"}
+ ${optionalString cfg.listen "bind=${cfg.address}"}
+ port=${toString cfg.port}
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
listen=${if cfg.listen then "1" else "0"}
${optionalString (cfg.discover != null) "discover=${if cfg.discover then "1" else "0"}"}
${lib.concatMapStrings (node: "addnode=${node}\n") cfg.addnodes}
# RPC server options
- ${optionalString (cfg.rpcthreads != null) "rpcthreads=${toString cfg.rpcthreads}"}
+ rpcbind=${cfg.rpc.address}
rpcport=${toString cfg.rpc.port}
+ rpcconnect=${cfg.rpc.address}
+ ${optionalString (cfg.rpc.threads != null) "rpcthreads=${toString cfg.rpc.threads}"}
rpcwhitelistdefault=0
${concatMapStrings (user: ''
${optionalString (!user.passwordHMACFromFile) "rpcauth=${user.name}:${passwordHMAC}"}
@@ -39,9 +41,7 @@ let
"rpcwhitelist=${user.name}:${lib.strings.concatStringsSep "," user.rpcwhitelist}"}
'') (builtins.attrValues cfg.rpc.users)
}
- rpcbind=${cfg.rpcbind}
- rpcconnect=${cfg.rpcbind}
- ${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
+ ${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpc.allowip}
# Wallet options
${optionalString (cfg.addresstype != null) "addresstype=${cfg.addresstype}"}
@@ -57,6 +57,16 @@ in {
options = {
services.bitcoind = {
enable = mkEnableOption "Bitcoin daemon";
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Address to listen for peer connections.";
+ };
+ port = mkOption {
+ type = types.port;
+ default = 8333;
+ description = "Port to listen for peer connections.";
+ };
package = mkOption {
type = types.package;
default = config.nix-bitcoin.pkgs.bitcoind;
@@ -77,13 +87,6 @@ in {
default = "/var/lib/bitcoind";
description = "The data directory for bitcoind.";
};
- bind = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = ''
- Bind to given address and always listen on it.
- '';
- };
user = mkOption {
type = types.str;
default = "bitcoin";
@@ -95,10 +98,29 @@ in {
description = "The group as which to run bitcoind.";
};
rpc = {
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = ''
+ Address to listen for JSON-RPC connections.
+ '';
+ };
port = mkOption {
type = types.port;
default = 8332;
- description = "Port on which to listen for JSON-RPC connections.";
+ description = "Port to listen for JSON-RPC connections.";
+ };
+ threads = mkOption {
+ type = types.nullOr types.ints.u16;
+ default = null;
+ description = "The number of threads to service RPC calls.";
+ };
+ allowip = mkOption {
+ type = types.listOf types.str;
+ default = [ "127.0.0.1" ];
+ description = ''
+ Allow JSON-RPC connections from specified sources.
+ '';
};
users = mkOption {
default = {};
@@ -144,25 +166,6 @@ in {
'';
};
};
- rpcthreads = mkOption {
- type = types.nullOr types.ints.u16;
- default = null;
- description = "Set the number of threads to service RPC calls";
- };
- rpcbind = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = ''
- Bind to given address to listen for JSON-RPC connections.
- '';
- };
- rpcallowip = mkOption {
- type = types.listOf types.str;
- default = [ "127.0.0.1" ];
- description = ''
- Allow JSON-RPC connections from specified source.
- '';
- };
regtest = mkOption {
type = types.bool;
default = false;
@@ -176,11 +179,6 @@ in {
readOnly = true;
default = mainnet: regtest: if cfg.regtest then regtest else mainnet;
};
- port = mkOption {
- type = types.nullOr types.port;
- default = null;
- description = "Override the default port on which to listen for connections.";
- };
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
diff --git a/modules/btcpayserver.nix b/modules/btcpayserver.nix
index 427a08a5..3600930a 100644
--- a/modules/btcpayserver.nix
+++ b/modules/btcpayserver.nix
@@ -14,6 +14,16 @@ in {
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";
@@ -29,16 +39,6 @@ in {
default = cfg.nbxplorer.user;
description = "The group as which to run nbxplorer.";
};
- bind = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = "The address on which to bind.";
- };
- port = mkOption {
- type = types.port;
- default = 24444;
- description = "Port on which to bind.";
- };
enable = mkOption {
# This option is only used by netns-isolation
internal = true;
@@ -49,6 +49,16 @@ in {
btcpayserver = {
enable = mkEnableOption "btcpayserver";
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Address to listen on.";
+ };
+ port = mkOption {
+ type = types.port;
+ default = 23000;
+ description = "Port to listen on.";
+ };
package = mkOption {
type = types.package;
default = nbPkgs.btcpayserver;
@@ -69,16 +79,6 @@ in {
default = cfg.btcpayserver.user;
description = "The group as which to run btcpayserver.";
};
- bind = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = "The address on which to bind.";
- };
- port = mkOption {
- type = types.port;
- default = 23000;
- description = "Port on which to bind.";
- };
lightningBackend = mkOption {
type = types.nullOr (types.enum [ "clightning" "lnd" ]);
default = null;
@@ -117,9 +117,9 @@ in {
configFile = builtins.toFile "config" ''
network=${config.services.bitcoind.network}
btcrpcuser=${cfg.bitcoind.rpc.users.btcpayserver.name}
- btcrpcurl=http://${config.services.bitcoind.rpcbind}:${toString cfg.bitcoind.rpc.port}
- btcnodeendpoint=${config.services.bitcoind.bind}:8333
- bind=${cfg.nbxplorer.bind}
+ btcrpcurl=http://${config.services.bitcoind.rpc.address}:${toString cfg.bitcoind.rpc.port}
+ btcnodeendpoint=${config.services.bitcoind.address}:${toString config.services.bitcoind.port}
+ bind=${cfg.nbxplorer.address}
port=${toString cfg.nbxplorer.port}
'';
in {
@@ -153,9 +153,9 @@ in {
network=${config.services.bitcoind.network}
postgres=User ID=${cfg.btcpayserver.user};Host=/run/postgresql;Database=btcpaydb
socksendpoint=${cfg.tor.client.socksListenAddress}
- btcexplorerurl=http://${cfg.nbxplorer.bind}:${toString cfg.nbxplorer.port}/
+ btcexplorerurl=http://${cfg.nbxplorer.address}:${toString cfg.nbxplorer.port}/
btcexplorercookiefile=${cfg.nbxplorer.dataDir}/${config.services.bitcoind.makeNetworkName "Main" "RegTest"}/.cookie
- bind=${cfg.btcpayserver.bind}
+ bind=${cfg.btcpayserver.address}
${optionalString (cfg.btcpayserver.rootpath != null) "rootpath=${cfg.btcpayserver.rootpath}"}
port=${toString cfg.btcpayserver.port}
'' + optionalString (cfg.btcpayserver.lightningBackend == "clightning") ''
@@ -163,7 +163,7 @@ in {
'');
lndConfig =
"btclightning=type=lnd-rest;" +
- "server=https://${toString cfg.lnd.listen}:${toString cfg.lnd.restPort}/;" +
+ "server=https://${cfg.lnd.restAddress}:${toString cfg.lnd.restPort}/;" +
"macaroonfilepath=/run/lnd/btcpayserver.macaroon;" +
"certthumbprint=";
in let self = {
diff --git a/modules/clightning.nix b/modules/clightning.nix
index 38f3b9dc..544782b9 100644
--- a/modules/clightning.nix
+++ b/modules/clightning.nix
@@ -6,15 +6,14 @@ let
cfg = config.services.clightning;
inherit (config) nix-bitcoin-services;
nbPkgs = config.nix-bitcoin.pkgs;
- onion-chef-service = (if cfg.announce-tor then [ "onion-chef.service" ] else []);
network = config.services.bitcoind.makeNetworkName "bitcoin" "regtest";
configFile = pkgs.writeText "config" ''
network=${network}
bitcoin-datadir=${config.services.bitcoind.dataDir}
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
always-use-proxy=${if cfg.always-use-proxy then "true" else "false"}
- bind-addr=${cfg.bind-addr}:${toString cfg.bindport}
- bitcoin-rpcconnect=${config.services.bitcoind.rpcbind}
+ bind-addr=${cfg.address}:${toString cfg.port}
+ bitcoin-rpcconnect=${config.services.bitcoind.rpc.address}
bitcoin-rpcport=${toString config.services.bitcoind.rpc.port}
bitcoin-rpcuser=${config.services.bitcoind.rpc.users.public.name}
rpc-file-mode=0660
@@ -29,13 +28,15 @@ in {
If enabled, the clightning service will be installed.
'';
};
- autolisten = mkOption {
- type = types.bool;
- default = false;
- description = ''
- Bind (and maybe announce) on IPv4 and IPv6 interfaces if no addr,
- bind-addr or announce-addr options are specified.
- '';
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "IP address or UNIX domain socket to listen for peer connections.";
+ };
+ port = mkOption {
+ type = types.port;
+ default = 9735;
+ description = "Port to listen for peer connections.";
};
proxy = mkOption {
type = types.nullOr types.str;
@@ -49,21 +50,6 @@ in {
Always use the *proxy*, even to connect to normal IP addresses (you can still connect to Unix domain sockets manually). This also disables all DNS lookups, to avoid leaking information.
'';
};
- bind-addr = mkOption {
- type = nbPkgs.lib.ipv4Address;
- default = "127.0.0.1";
- description = "Set an IP address or UNIX domain socket to listen to";
- };
- bindport = mkOption {
- type = types.port;
- default = 9735;
- description = "Set a Port to listen to locally";
- };
- announce-tor = mkOption {
- type = types.bool;
- default = false;
- description = "Announce clightning Tor Hidden Service";
- };
dataDir = mkOption {
type = types.path;
default = "/var/lib/clightning";
@@ -97,11 +83,24 @@ in {
'';
description = "Binary to connect with the clightning instance.";
};
- enforceTor = nix-bitcoin-services.enforceTor;
+ getPublicAddressCmd = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ Bash expression which outputs the public service address to announce to peers.
+ If left empty, no address is announced.
+ '';
+ };
+ inherit (nix-bitcoin-services) enforceTor;
};
config = mkIf cfg.enable {
- services.bitcoind.enable = true;
+ services.bitcoind = {
+ enable = true;
+ # Increase rpc thread count due to reports that lightning implementations fail
+ # under high bitcoind rpc load
+ rpc.threads = 16;
+ };
environment.systemPackages = [ nbPkgs.clightning (hiPrio cfg.cli) ];
users.users.${cfg.user} = {
@@ -116,21 +115,25 @@ in {
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
- services.onion-chef.access.clightning = if cfg.announce-tor then [ "clightning" ] else [];
systemd.services.clightning = {
description = "Run clightningd";
path = [ nbPkgs.bitcoind ];
wantedBy = [ "multi-user.target" ];
- requires = [ "bitcoind.service" ] ++ onion-chef-service;
- after = [ "bitcoind.service" ] ++ onion-chef-service;
+ requires = [ "bitcoind.service" ];
+ after = [ "bitcoind.service" ];
preStart = ''
cp ${configFile} ${cfg.dataDir}/config
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
# The RPC socket has to be removed otherwise we might have stale sockets
rm -f ${cfg.networkDir}/lightning-rpc
chmod 640 ${cfg.dataDir}/config
- echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-public)" >> '${cfg.dataDir}/config'
- ${optionalString cfg.announce-tor "echo announce-addr=$(cat /var/lib/onion-chef/clightning/clightning) >> '${cfg.dataDir}/config'"}
+ {
+ echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-public)"
+ ${optionalString (cfg.getPublicAddressCmd != "") ''
+ echo "announce-addr=$(${cfg.getPublicAddressCmd})"
+ ''}
+ } >> '${cfg.dataDir}/config'
+
'';
serviceConfig = nix-bitcoin-services.defaultHardening // {
ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
diff --git a/modules/default.nix b/modules/default.nix
index 72d76615..cc013a5d 100644
--- a/modules/default.nix
+++ b/modules/default.nix
@@ -6,7 +6,6 @@
electrs = ./electrs.nix;
liquid = ./liquid.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;
lnd = ./lnd.nix;
diff --git a/modules/electrs.nix b/modules/electrs.nix
index 5258317c..5d03c10f 100644
--- a/modules/electrs.nix
+++ b/modules/electrs.nix
@@ -9,6 +9,16 @@ let
in {
options.services.electrs = {
enable = mkEnableOption "electrs";
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Address to listen for RPC connections.";
+ };
+ port = mkOption {
+ type = types.port;
+ default = 50001;
+ description = "RPC port.";
+ };
dataDir = mkOption {
type = types.path;
default = "/var/lib/electrs";
@@ -31,16 +41,6 @@ in {
If enabled, the electrs service will sync faster on high-memory systems (≥ 8GB).
'';
};
- address = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = "RPC and monitoring listening address.";
- };
- port = mkOption {
- type = types.port;
- default = 50001;
- description = "RPC port.";
- };
monitoringPort = mkOption {
type = types.port;
default = 4224;
@@ -95,7 +95,7 @@ in {
--daemon-dir='${bitcoind.dataDir}' \
--electrum-rpc-addr=${cfg.address}:${toString cfg.port} \
--monitoring-addr=${cfg.address}:${toString cfg.monitoringPort} \
- --daemon-rpc-addr=${bitcoind.rpcbind}:${toString bitcoind.rpc.port} \
+ --daemon-rpc-addr=${bitcoind.rpc.address}:${toString bitcoind.rpc.port} \
${cfg.extraArgs}
'';
User = cfg.user;
diff --git a/modules/joinmarket.nix b/modules/joinmarket.nix
index 66d36a7d..d1c23674 100644
--- a/modules/joinmarket.nix
+++ b/modules/joinmarket.nix
@@ -21,7 +21,7 @@ let
[BLOCKCHAIN]
blockchain_source = bitcoin-rpc
network = ${bitcoind.network}
- rpc_host = ${bitcoind.rpcbind}
+ rpc_host = ${bitcoind.rpc.address}
rpc_port = ${toString bitcoind.rpc.port}
rpc_user = ${bitcoind.rpc.users.privileged.name}
@@RPC_PASSWORD@@
diff --git a/modules/lightning-loop.nix b/modules/lightning-loop.nix
index 87022975..37d94486 100644
--- a/modules/lightning-loop.nix
+++ b/modules/lightning-loop.nix
@@ -17,7 +17,7 @@ let
tlscertpath=${secretsDir}/loop-cert
tlskeypath=${secretsDir}/loop-key
- lnd.host=${config.services.lnd.rpclisten}:${toString config.services.lnd.rpcPort}
+ lnd.host=${config.services.lnd.rpcAddress}:${toString config.services.lnd.rpcPort}
lnd.macaroondir=${config.services.lnd.networkDir}
lnd.tlspath=${secretsDir}/lnd-cert
diff --git a/modules/liquid.nix b/modules/liquid.nix
index ec3ee6e4..6e833217 100644
--- a/modules/liquid.nix
+++ b/modules/liquid.nix
@@ -16,23 +16,22 @@ let
${optionalString (cfg.validatepegin != null) "validatepegin=${if cfg.validatepegin then "1" else "0"}"}
# Connection options
- ${optionalString cfg.listen "bind=${cfg.bind}"}
- ${optionalString (cfg.port != null) "port=${toString cfg.port}"}
+ ${optionalString cfg.listen "bind=${cfg.address}"}
+ port=${toString cfg.port}
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
listen=${if cfg.listen then "1" else "0"}
# RPC server options
- ${optionalString (cfg.rpc.port != null) "rpcport=${toString cfg.rpc.port}"}
+ rpcport=${toString cfg.rpc.port}
${concatMapStringsSep "\n"
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
(attrValues cfg.rpc.users)
}
- rpcbind=${cfg.rpcbind}
- rpcconnect=${cfg.rpcbind}
+ rpcbind=${cfg.rpc.address}
+ rpcconnect=${cfg.rpc.address}
${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
- ${optionalString (cfg.rpcuser != null) "rpcuser=${cfg.rpcuser}"}
- ${optionalString (cfg.rpcpassword != null) "rpcpassword=${cfg.rpcpassword}"}
- mainchainrpchost=${config.services.bitcoind.rpcbind}
+ rpcuser=${cfg.rpcuser}
+ mainchainrpchost=${config.services.bitcoind.rpc.address}
mainchainrpcport=${toString config.services.bitcoind.rpc.port}
mainchainrpcuser=${config.services.bitcoind.rpc.users.public.name}
@@ -71,7 +70,16 @@ in {
services.liquidd = {
enable = mkEnableOption "Liquid sidechain";
-
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Address to listen for peer connections.";
+ };
+ port = mkOption {
+ type = types.port;
+ default = 7042;
+ description = "Override the default port on which to listen for connections.";
+ };
extraConfig = mkOption {
type = types.lines;
default = "";
@@ -88,14 +96,6 @@ in {
default = "/var/lib/liquidd";
description = "The data directory for liquidd.";
};
- bind = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = ''
- Bind to given address and always listen on it.
- '';
- };
-
user = mkOption {
type = types.str;
default = "liquid";
@@ -106,12 +106,16 @@ in {
default = cfg.user;
description = "The group as which to run liquidd.";
};
-
rpc = {
+ address = mkOption {
+ type = types.str;
+ default = "127.0.0.1";
+ description = "Address to listen for JSON-RPC connections.";
+ };
port = mkOption {
- type = types.nullOr types.port;
- default = null;
- description = "Override the default port on which to listen for JSON-RPC connections.";
+ type = types.port;
+ default = 7041;
+ description = "Port to listen for JSON-RPC connections.";
};
users = mkOption {
default = {};
@@ -125,14 +129,6 @@ in {
'';
};
};
-
- rpcbind = mkOption {
- type = types.str;
- default = "127.0.0.1";
- description = ''
- Bind to given address to listen for JSON-RPC connections.
- '';
- };
rpcallowip = mkOption {
type = types.listOf types.str;
default = [ "127.0.0.1" ];
@@ -141,25 +137,15 @@ in {
'';
};
rpcuser = mkOption {
- type = types.nullOr types.str;
- default = null;
+ type = types.str;
+ default = "liquidrpc";
description = "Username for JSON-RPC connections";
};
- rpcpassword = mkOption {
- type = types.nullOr types.str;
- default = null;
- description = "Password for JSON-RPC connections";
- };
testnet = mkOption {
type = types.bool;
default = false;
description = "Whether to use the test chain.";
};
- port = mkOption {
- type = types.nullOr types.port;
- default = null;
- description = "Override the default port on which to listen for connections.";
- };
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
diff --git a/modules/lnd.nix b/modules/lnd.nix
index 7df79349..e8fb9c7b 100644
--- a/modules/lnd.nix
+++ b/modules/lnd.nix
@@ -8,8 +8,7 @@ let
secretsDir = config.nix-bitcoin.secretsDir;
bitcoind = config.services.bitcoind;
- bitcoindRpcAddress = bitcoind.rpcbind;
- onion-chef-service = (if cfg.announce-tor then [ "onion-chef.service" ] else []);
+ bitcoindRpcAddress = bitcoind.rpc.address;
networkDir = "${cfg.dataDir}/chain/bitcoin/${bitcoind.network}";
configFile = pkgs.writeText "lnd.conf" ''
datadir=${cfg.dataDir}
@@ -17,9 +16,9 @@ let
tlscertpath=${secretsDir}/lnd-cert
tlskeypath=${secretsDir}/lnd-key
- listen=${toString cfg.listen}:${toString cfg.listenPort}
- rpclisten=${cfg.rpclisten}:${toString cfg.rpcPort}
- restlisten=${cfg.restlisten}:${toString cfg.restPort}
+ listen=${toString cfg.address}:${toString cfg.port}
+ rpclisten=${cfg.rpcAddress}:${toString cfg.rpcPort}
+ restlisten=${cfg.restAddress}:${toString cfg.restPort}
bitcoin.${bitcoind.network}=1
bitcoin.active=1
@@ -55,50 +54,43 @@ in {
default = networkDir;
description = "The network data directory.";
};
- listen = mkOption {
- type = config.nix-bitcoin.pkgs.lib.ipv4Address;
+ address = mkOption {
+ type = types.str;
default = "localhost";
- description = "Bind to given address to listen to peer connections";
+ description = "Address to listen for peer connections";
};
- listenPort = mkOption {
+ port = mkOption {
type = types.port;
default = 9735;
- description = "Bind to given port to listen to peer connections";
+ description = "Port to listen for peer connections";
};
- rpclisten = mkOption {
+ rpcAddress = mkOption {
type = types.str;
default = "localhost";
- description = ''
- Bind to given address to listen to RPC connections.
- '';
+ description = "Address to listen for RPC connections.";
};
- restlisten = mkOption {
+ rpcPort = mkOption {
+ type = types.port;
+ default = 10009;
+ description = "Port to listen for gRPC connections.";
+ };
+ restAddress = mkOption {
type = types.str;
default = "localhost";
description = ''
- Bind to given address to listen to REST connections.
+ Address to listen for REST connections.
'';
};
- rpcPort = mkOption {
- type = types.port;
- default = 10009;
- description = "Port on which to listen for gRPC connections.";
- };
restPort = mkOption {
type = types.port;
default = 8080;
- description = "Port on which to listen for REST connections.";
+ description = "Port to listen for REST connections.";
};
tor-socks = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
description = "Set a socks proxy to use to connect to Tor nodes";
};
- announce-tor = mkOption {
- type = types.bool;
- default = false;
- description = "Announce LND Tor Hidden Service";
- };
macaroons = mkOption {
default = {};
type = with types; attrsOf (submodule {
@@ -138,13 +130,21 @@ in {
# Switch user because lnd makes datadir contents readable by user only
''
sudo -u lnd ${cfg.package}/bin/lncli \
- --rpcserver ${cfg.rpclisten}:${toString cfg.rpcPort} \
+ --rpcserver ${cfg.rpcAddress}:${toString cfg.rpcPort} \
--tlscertpath '${secretsDir}/lnd-cert' \
--macaroonpath '${networkDir}/admin.macaroon' "$@"
'';
description = "Binary to connect with the lnd instance.";
};
- enforceTor = nix-bitcoin-services.enforceTor;
+ getPublicAddressCmd = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ Bash expression which outputs the public service address to announce to peers.
+ If left empty, no address is announced.
+ '';
+ };
+ inherit (nix-bitcoin-services) enforceTor;
};
config = mkIf cfg.enable {
@@ -154,7 +154,12 @@ in {
}
];
- services.bitcoind.enable = true;
+ services.bitcoind = {
+ enable = true;
+ # Increase rpc thread count due to reports that lightning implementations fail
+ # under high bitcoind rpc load
+ rpc.threads = 16;
+ };
environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ];
@@ -167,16 +172,19 @@ in {
zmqpubrawtx = "tcp://${bitcoindRpcAddress}:28333";
};
- services.onion-chef.access.lnd = if cfg.announce-tor then [ "lnd" ] else [];
systemd.services.lnd = {
description = "Run LND";
wantedBy = [ "multi-user.target" ];
- requires = [ "bitcoind.service" ] ++ onion-chef-service;
- after = [ "bitcoind.service" ] ++ onion-chef-service;
+ requires = [ "bitcoind.service" ];
+ after = [ "bitcoind.service" ];
preStart = ''
install -m600 ${configFile} '${cfg.dataDir}/lnd.conf'
- echo "bitcoind.rpcpass=$(cat ${secretsDir}/bitcoin-rpcpassword-public)" >> '${cfg.dataDir}/lnd.conf'
- ${optionalString cfg.announce-tor "echo externalip=$(cat /var/lib/onion-chef/lnd/lnd) >> '${cfg.dataDir}/lnd.conf'"}
+ {
+ echo "bitcoind.rpcpass=$(cat ${secretsDir}/bitcoin-rpcpassword-public)"
+ ${optionalString (cfg.getPublicAddressCmd != "") ''
+ echo "externalip=$(${cfg.getPublicAddressCmd})"
+ ''}
+ } >> '${cfg.dataDir}/lnd.conf'
'';
serviceConfig = nix-bitcoin-services.defaultHardening // {
RuntimeDirectory = "lnd"; # Only used to store custom macaroons
@@ -187,12 +195,12 @@ in {
RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}";
ExecStartPost = let
- restUrl = "https://${cfg.restlisten}:${toString cfg.restPort}/v1";
+ restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1";
in [
# Run fully privileged for secrets dir write access
"+${nix-bitcoin-services.script ''
attempts=250
- while ! { exec 3>/dev/tcp/${cfg.restlisten}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
+ while ! { exec 3>/dev/tcp/${cfg.restAddress}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
sleep 0.1
done
@@ -234,7 +242,7 @@ in {
fi
# Wait until the RPC port is open
- while ! { exec 3>/dev/tcp/${cfg.rpclisten}/${toString cfg.rpcPort}; } &>/dev/null; do
+ while ! { exec 3>/dev/tcp/${cfg.rpcAddress}/${toString cfg.rpcPort}; } &>/dev/null; do
sleep 0.1
done
diff --git a/modules/modules.nix b/modules/modules.nix
index 96fc869b..4788da6e 100644
--- a/modules/modules.nix
+++ b/modules/modules.nix
@@ -24,9 +24,11 @@ with lib;
# Support features
./versioning.nix
./security.nix
+ ./onion-addresses.nix
+ ./onion-services.nix
./netns-isolation.nix
+ ./nodeinfo.nix
./backups.nix
- ./onion-chef.nix
];
disabledModules = [ "services/networking/bitcoind.nix" ];
@@ -58,11 +60,11 @@ with lib;
config = {
assertions = [
- { assertion = (config.services.lnd.enable -> ( !config.services.clightning.enable || config.services.clightning.bindport != config.services.lnd.listenPort));
+ { assertion = (config.services.lnd.enable -> ( !config.services.clightning.enable || config.services.clightning.port != config.services.lnd.port));
message = ''
LND and clightning can't both bind to lightning port 9735. Either
disable LND/clightning or change services.clightning.bindPort or
- services.lnd.listenPort to a port other than 9735.
+ services.lnd.port to a port other than 9735.
'';
}
];
diff --git a/modules/netns-isolation.nix b/modules/netns-isolation.nix
index 0d335f58..36e5300f 100644
--- a/modules/netns-isolation.nix
+++ b/modules/netns-isolation.nix
@@ -245,26 +245,26 @@ in {
};
services.bitcoind = {
- bind = netns.bitcoind.address;
- rpcbind = netns.bitcoind.address;
- rpcallowip = [
+ address = netns.bitcoind.address;
+ rpc.address = netns.bitcoind.address;
+ rpc.allowip = [
bridgeIp # For operator user
netns.bitcoind.address
] ++ map (n: netns.${n}.address) netns.bitcoind.availableNetns;
};
systemd.services.bitcoind-import-banlist.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-bitcoind";
- services.clightning.bind-addr = netns.clightning.address;
+ services.clightning.address = netns.clightning.address;
services.lnd = {
- listen = netns.lnd.address;
- rpclisten = netns.lnd.address;
- restlisten = netns.lnd.address;
+ address = netns.lnd.address;
+ rpcAddress = netns.lnd.address;
+ restAddress = netns.lnd.address;
};
services.liquidd = {
- bind = netns.liquidd.address;
- rpcbind = netns.liquidd.address;
+ address = netns.liquidd.address;
+ rpc.address = netns.liquidd.address;
rpcallowip = [
bridgeIp # For operator user
netns.liquidd.address
@@ -274,14 +274,14 @@ in {
services.electrs.address = netns.electrs.address;
services.spark-wallet = {
- host = netns.spark-wallet.address;
+ address = netns.spark-wallet.address;
extraArgs = "--no-tls";
};
services.lightning-loop.rpcAddress = netns.lightning-loop.address;
- services.nbxplorer.bind = netns.nbxplorer.address;
- services.btcpayserver.bind = netns.btcpayserver.address;
+ services.nbxplorer.address = netns.nbxplorer.address;
+ services.btcpayserver.address = netns.btcpayserver.address;
services.joinmarket.cliExec = mkCliExec "joinmarket";
systemd.services.joinmarket-yieldgenerator.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-joinmarket";
diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix
deleted file mode 100644
index 4224243c..00000000
--- a/modules/nix-bitcoin-webindex.nix
+++ /dev/null
@@ -1,105 +0,0 @@
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
- cfg = config.services.nix-bitcoin-webindex;
- inherit (config) nix-bitcoin-services;
- indexFile = pkgs.writeText "index.html" ''
-
-
-
-
- nix-bitcoin
-
-
-
-
- lightning node: CLIGHTNING_ID
-
-
-
-
- '';
- createWebIndex = pkgs.writeText "make-index.sh" ''
- set -e
- cp ${indexFile} /var/www/index.html
- chown -R nginx:nginx /var/www/
- nodeinfo
- . <(nodeinfo)
- sed -i "s/CLIGHTNING_ID/$CLIGHTNING_ID/g" /var/www/index.html
- '';
-in {
- options.services.nix-bitcoin-webindex = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- If enabled, the webindex service will be installed.
- '';
- };
- host = mkOption {
- type = types.str;
- default = if config.nix-bitcoin.netns-isolation.enable then
- config.nix-bitcoin.netns-isolation.netns.nginx.address
- else
- "localhost";
- description = "HTTP server listen address.";
- };
- enforceTor = nix-bitcoin-services.enforceTor;
- };
-
- config = mkIf cfg.enable {
- assertions = [
- { assertion = config.services.clightning.enable;
- message = "nix-bitcoin-webindex requires clightning.";
- }
- ];
-
- systemd.tmpfiles.rules = [
- "d /var/www 0755 nginx nginx - -"
- ];
-
- services.nginx = {
- enable = true;
- virtualHosts."_" = {
- root = "/var/www";
- };
- };
- services.tor.hiddenServices.nginx = {
- map = [{
- port = 80; toHost = cfg.host;
- } {
- port = 443; toHost = cfg.host;
- }];
- version = 3;
- };
-
- # create-web-index
- systemd.services.create-web-index = {
- description = "Get node info";
- wantedBy = [ "multi-user.target" ];
- path = with pkgs; [
- config.programs.nodeinfo
- jq
- sudo
- ] ++ optional config.services.lnd.enable config.services.lnd.cli
- ++ optional config.services.clightning.enable config.services.clightning.cli;
- serviceConfig = nix-bitcoin-services.defaultHardening // {
- ExecStart="${pkgs.bash}/bin/bash ${createWebIndex}";
- User = "root";
- Type = "simple";
- RemainAfterExit="yes";
- Restart = "on-failure";
- RestartSec = "10s";
- PrivateNetwork = "true"; # This service needs no network access
- PrivateUsers = "false";
- ReadWritePaths = "/var/www";
- CapabilityBoundingSet = "CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_SYS_ADMIN CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER";
- } // (if cfg.enforceTor
- then nix-bitcoin-services.allowTor
- else nix-bitcoin-services.allowAnyIP
- );
- };
- };
-}
diff --git a/modules/nodeinfo.nix b/modules/nodeinfo.nix
index 86f41741..afd3fa67 100644
--- a/modules/nodeinfo.nix
+++ b/modules/nodeinfo.nix
@@ -1,74 +1,117 @@
{ config, lib, pkgs, ... }:
with lib;
-
let
- operatorName = config.nix-bitcoin.operator.name;
+ cfg = config.nix-bitcoin.nodeinfo;
+
+ # Services included in the output
+ services = {
+ bitcoind = mkInfo "";
+ clightning = mkInfo ''
+ info["nodeid"] = shell("lightning-cli getinfo | jq -r '.id'")
+ if 'onion_address' in info:
+ info["id"] = f"{info['nodeid']}@{info['onion_address']}"
+ '';
+ lnd = mkInfo ''
+ info["nodeid"] = shell("lightning-cli getinfo | jq -r '.id'")
+ '';
+ electrs = mkInfo "";
+ spark-wallet = mkInfo "";
+ btcpayserver = mkInfo "";
+ liquidd = mkInfo "";
+ # Only add sshd when it has an onion service
+ sshd = name: cfg: mkIfOnionPort "sshd" (onionPort: ''
+ add_service("sshd", """set_onion_address(info, "sshd", ${onionPort})""")
+ '');
+ };
+
script = pkgs.writeScriptBin "nodeinfo" ''
- set -eo pipefail
-
- BITCOIND_ONION="$(cat /var/lib/onion-chef/${operatorName}/bitcoind)"
- echo BITCOIND_ONION="$BITCOIND_ONION"
-
- if systemctl is-active --quiet clightning; then
- CLIGHTNING_NODEID=$(lightning-cli getinfo | jq -r '.id')
- CLIGHTNING_ONION="$(cat /var/lib/onion-chef/${operatorName}/clightning)"
- CLIGHTNING_ID="$CLIGHTNING_NODEID@$CLIGHTNING_ONION:9735"
- echo CLIGHTNING_NODEID="$CLIGHTNING_NODEID"
- echo CLIGHTNING_ONION="$CLIGHTNING_ONION"
- echo CLIGHTNING_ID="$CLIGHTNING_ID"
- fi
-
- if systemctl is-active --quiet lnd; then
- LND_NODEID=$(lncli getinfo | jq -r '.uris[0]')
- echo LND_NODEID="$LND_NODEID"
- fi
-
- NGINX_ONION_FILE=/var/lib/onion-chef/${operatorName}/nginx
- if [ -e "$NGINX_ONION_FILE" ]; then
- NGINX_ONION="$(cat $NGINX_ONION_FILE)"
- echo NGINX_ONION="$NGINX_ONION"
- fi
-
- LIQUIDD_ONION_FILE=/var/lib/onion-chef/${operatorName}/liquidd
- if [ -e "$LIQUIDD_ONION_FILE" ]; then
- LIQUIDD_ONION="$(cat $LIQUIDD_ONION_FILE)"
- echo LIQUIDD_ONION="$LIQUIDD_ONION"
- fi
-
- SPARKWALLET_ONION_FILE=/var/lib/onion-chef/${operatorName}/spark-wallet
- if [ -e "$SPARKWALLET_ONION_FILE" ]; then
- SPARKWALLET_ONION="$(cat $SPARKWALLET_ONION_FILE)"
- echo SPARKWALLET_ONION="http://$SPARKWALLET_ONION"
- fi
-
- ELECTRS_ONION_FILE=/var/lib/onion-chef/${operatorName}/electrs
- if [ -e "$ELECTRS_ONION_FILE" ]; then
- ELECTRS_ONION="$(cat $ELECTRS_ONION_FILE)"
- echo ELECTRS_ONION="$ELECTRS_ONION"
- fi
-
- BTCPAYSERVER_ONION_FILE=/var/lib/onion-chef/${operatorName}/btcpayserver
- if [ -e "$BTCPAYSERVER_ONION_FILE" ]; then
- BTCPAYSERVER_ONION="$(cat $BTCPAYSERVER_ONION_FILE)"
- echo BTCPAYSERVER_ONION="$BTCPAYSERVER_ONION"
- fi
-
- SSHD_ONION_FILE=/var/lib/onion-chef/${operatorName}/sshd
- if [ -e "$SSHD_ONION_FILE" ]; then
- SSHD_ONION="$(cat $SSHD_ONION_FILE)"
- echo SSHD_ONION="$SSHD_ONION"
- fi
+ #!${pkgs.python3}/bin/python
+
+ import json
+ import subprocess
+ from collections import OrderedDict
+
+ def success(*args):
+ return subprocess.call(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
+
+ def is_active(unit):
+ return success("systemctl", "is-active", "--quiet", unit)
+
+ def is_enabled(unit):
+ return success("systemctl", "is-enabled", "--quiet", unit)
+
+ def cmd(*args):
+ return subprocess.run(args, stdout=subprocess.PIPE).stdout.decode('utf-8')
+
+ def shell(*args):
+ return cmd("bash", "-c", *args).strip()
+
+ infos = OrderedDict()
+ operator = "${config.nix-bitcoin.operator.name}"
+
+ def set_onion_address(info, name, port):
+ path = f"/var/lib/onion-addresses/{operator}/{name}"
+ try:
+ with open(path, "r") as f:
+ onion_address = f.read().strip()
+ except OSError:
+ print(f"error reading file {path}", file=sys.stderr)
+ return
+ info["onion_address"] = f"{onion_address}:{port}"
+
+ def add_service(service, make_info):
+ if not is_active(service):
+ infos[service] = "service is not running"
+ else:
+ info = OrderedDict()
+ exec(make_info, globals(), locals())
+ infos[service] = info
+
+ if is_enabled("onion-adresses") and not is_active("onion-adresses"):
+ print("error: service 'onion-adresses' is not running")
+ exit(1)
+
+ ${concatStrings infos}
+
+ print(json.dumps(infos, indent=2))
'';
+
+ infos = map (service:
+ let cfg = config.services.${service};
+ in optionalString cfg.enable (services.${service} service cfg)
+ ) (builtins.attrNames services);
+
+ mkInfo = extraCode: name: cfg:
+ ''
+ add_service("${name}", """
+ info["local_address"] = "${cfg.address}:${toString cfg.port}"
+ '' + mkIfOnionPort name (onionPort: ''
+ set_onion_address(info, "${name}", ${onionPort})
+ '') + extraCode + ''
+
+ """)
+ '';
+
+ mkIfOnionPort = name: fn:
+ if hiddenServices ? ${name} then
+ fn (toString (builtins.elemAt hiddenServices.${name}.map 0).port)
+ else
+ "";
+
+ inherit (config.services.tor) hiddenServices;
in {
options = {
- programs.nodeinfo = mkOption {
- readOnly = true;
- default = script;
+ nix-bitcoin.nodeinfo = {
+ enable = mkEnableOption "nodeinfo";
+ program = mkOption {
+ readOnly = true;
+ default = script;
+ };
};
};
config = {
- environment.systemPackages = [ script ];
+ environment.systemPackages = optional cfg.enable script;
};
}
diff --git a/modules/obsolete-options.nix b/modules/obsolete-options.nix
new file mode 100644
index 00000000..4c39affc
--- /dev/null
+++ b/modules/obsolete-options.nix
@@ -0,0 +1,28 @@
+{ lib, ... }:
+
+with lib;
+let
+ mkRenamedAnnounceTorOption = service:
+ # use mkRemovedOptionModule because mkRenamedOptionModule fails with an infinite recursion error
+ mkRemovedOptionModule [ "services" service "announce-tor" ] ''
+ Use option `nix-bitcoin.onionServices.${service}.public` instead.
+ '';
+in {
+ imports = [
+ (mkRenamedOptionModule [ "services" "bitcoind" "bind" ] [ "services" "bitcoind" "address" ])
+ (mkRenamedOptionModule [ "services" "bitcoind" "rpcallowip" ] [ "services" "bitcoind" "rpc" "allowip" ])
+ (mkRenamedOptionModule [ "services" "bitcoind" "rpcthreads" ] [ "services" "bitcoind" "rpc" "threads" ])
+ (mkRenamedOptionModule [ "services" "clightning" "bind-addr" ] [ "services" "clightning" "address" ])
+ (mkRenamedOptionModule [ "services" "clightning" "bindport" ] [ "services" "clightning" "port" ])
+ (mkRenamedOptionModule [ "services" "spark-wallet" "host" ] [ "services" "spark-wallet" "address" ])
+ (mkRenamedOptionModule [ "services" "lnd" "rpclisten" ] [ "services" "lnd" "rpcAddress" ])
+ (mkRenamedOptionModule [ "services" "lnd" "listen" ] [ "services" "lnd" "address" ])
+ (mkRenamedOptionModule [ "services" "lnd" "listenPort" ] [ "services" "lnd" "port" ])
+ (mkRenamedOptionModule [ "services" "btcpayserver" "bind" ] [ "services" "btcpayserver" "address" ])
+ (mkRenamedOptionModule [ "services" "liquidd" "bind" ] [ "services" "liquidd" "address" ])
+ (mkRenamedOptionModule [ "services" "liquidd" "rpcbind" ] [ "services" "liquidd" "rpc" "address" ])
+
+ (mkRenamedAnnounceTorOption "clightning")
+ (mkRenamedAnnounceTorOption "lnd")
+ ];
+}
diff --git a/modules/onion-addresses.nix b/modules/onion-addresses.nix
new file mode 100644
index 00000000..f7153370
--- /dev/null
+++ b/modules/onion-addresses.nix
@@ -0,0 +1,76 @@
+# This module enables unprivileged users to read onion addresses.
+# By default, onion addresses in /var/lib/tor/onion are only readable by the
+# tor user.
+# The included service copies onion addresses to /var/lib/onion-addresses//
+# and sets permissions according to option 'access'.
+
+{ config, lib, ... }:
+
+with lib;
+
+let
+ cfg = config.nix-bitcoin.onionAddresses;
+ inherit (config) nix-bitcoin-services;
+in {
+ options.nix-bitcoin.onionAddresses = {
+ access = mkOption {
+ type = with types; attrsOf (listOf str);
+ default = {};
+ description = ''
+ This option controls who is allowed to access onion addresses.
+ For example, the following allows user 'myuser' to access bitcoind
+ and clightning onion addresses:
+ {
+ "myuser" = [ "bitcoind" "clightning" ];
+ };
+ The onion hostnames can then be read from
+ /var/lib/onion-addresses/myuser.
+ '';
+ };
+ dataDir = mkOption {
+ readOnly = true;
+ default = "/var/lib/onion-addresses";
+ };
+ };
+
+ config = mkIf (cfg.access != {}) {
+ systemd.services.onion-addresses = {
+ wantedBy = [ "tor.service" ];
+ bindsTo = [ "tor.service" ];
+ after = [ "tor.service" ];
+ serviceConfig = nix-bitcoin-services.defaultHardening // {
+ Type = "oneshot";
+ RemainAfterExit = true;
+ StateDirectory = "onion-addresses";
+ PrivateNetwork = "true"; # This service needs no network access
+ PrivateUsers = "false";
+ CapabilityBoundingSet = "CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER";
+ };
+ script = ''
+ # Wait until tor is up
+ until [[ -e /var/lib/tor/state ]]; do sleep 0.1; done
+
+ cd ${cfg.dataDir}
+ rm -rf *
+
+ ${concatMapStrings
+ (user: ''
+ mkdir -p -m 0700 ${user}
+ chown ${user} ${user}
+ ${concatMapStrings
+ (service: ''
+ onionFile=/var/lib/tor/onion/${service}/hostname
+ if [[ -e $onionFile ]]; then
+ cp $onionFile ${user}/${service}
+ chown ${user} ${user}/${service}
+ fi
+ '')
+ cfg.access.${user}
+ }
+ '')
+ (builtins.attrNames cfg.access)
+ }
+ '';
+ };
+ };
+}
diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix
deleted file mode 100644
index 2fe38394..00000000
--- a/modules/onion-chef.nix
+++ /dev/null
@@ -1,90 +0,0 @@
-# The onion chef module allows unprivileged users to read onion hostnames.
-# By default the onion hostnames in /var/lib/tor/onion are only readable by the
-# tor user. The onion chef copies the onion hostnames into into
-# /var/lib/onion-chef and sets permissions according to the access option.
-
-{ config, lib, pkgs, ... }:
-
-with lib;
-
-let
- cfg = config.services.onion-chef;
- inherit (config) nix-bitcoin-services;
- dataDir = "/var/lib/onion-chef/";
- onion-chef-script = pkgs.writeScript "onion-chef.sh" ''
- # wait until tor is up
- until ls -l /var/lib/tor/state; do sleep 1; done
-
- cd ${dataDir}
-
- # Create directory for every user and set permissions
- ${ builtins.foldl'
- (x: user: x +
- ''
- mkdir -p -m 0700 ${user}
- chown ${user} ${user}
- # Copy onion hostnames into the user's directory
- ${ builtins.foldl'
- (x: onion: x +
- ''
- ONION_FILE=/var/lib/tor/onion/${onion}/hostname
- if [ -e "$ONION_FILE" ]; then
- cp $ONION_FILE ${user}/${onion}
- chown ${user} ${user}/${onion}
- fi
- '')
- ""
- (builtins.getAttr user cfg.access)
- }
- '')
- ""
- (builtins.attrNames cfg.access)
- }
- '';
-in {
- options.services.onion-chef = {
- enable = mkOption {
- type = types.bool;
- default = false;
- description = ''
- If enabled, the onion-chef service will be installed.
- '';
- };
- access = mkOption {
- type = types.attrs;
- default = {};
- description = ''
- This option controls who is allowed to access onion hostnames. For
- example the following allows the user operator to access the bitcoind
- and clightning onion.
- {
- "operator" = [ "bitcoind" "clightning" ];
- };
- The onion hostnames can then be read from
- /var/lib/onion-chef/.
- '';
- };
- };
-
- config = mkIf cfg.enable {
- systemd.tmpfiles.rules = [
- "d '${dataDir}' 0755 root root - -"
- ];
-
- systemd.services.onion-chef = {
- description = "Run onion-chef";
- wantedBy = [ "tor.service" ];
- bindsTo = [ "tor.service" ];
- after = [ "tor.service" ];
- serviceConfig = nix-bitcoin-services.defaultHardening // {
- ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}";
- Type = "oneshot";
- RemainAfterExit = true;
- PrivateNetwork = "true"; # This service needs no network access
- PrivateUsers = "false";
- ReadWritePaths = "${dataDir}";
- CapabilityBoundingSet = "CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER";
- };
- };
- };
-}
diff --git a/modules/onion-services.nix b/modules/onion-services.nix
new file mode 100644
index 00000000..db0dac17
--- /dev/null
+++ b/modules/onion-services.nix
@@ -0,0 +1,121 @@
+# This module creates onion-services for NixOS services.
+# An onion service can be enabled for every service that defines
+# options 'address', 'port' and optionally 'getPublicAddressCmd'.
+#
+# See it in use at ./presets/enable-tor.nix
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+let
+ cfg = config.nix-bitcoin.onionServices;
+
+ services = builtins.attrNames cfg;
+
+ activeServices = builtins.filter (service:
+ config.services.${service}.enable && cfg.${service}.enable
+ ) services;
+
+ publicServices = builtins.filter (service: cfg.${service}.public) activeServices;
+in {
+ options.nix-bitcoin.onionServices = mkOption {
+ default = {};
+ type = with types; attrsOf (submodule (
+ { config, ... }: {
+ options = {
+ enable = mkOption {
+ type = types.bool;
+ default = config.public;
+ description = ''
+ Create an onion service for the given service.
+ The service must define options 'address' and 'port'.
+ '';
+ };
+ public = mkOption {
+ type = types.bool;
+ default = false;
+ description = ''
+ Make the onion address accessible to the service.
+ If enabled, the onion service is automatically enabled.
+ Only available for services that define option `getPublicAddressCmd`.
+ '';
+ };
+ externalPort = mkOption {
+ type = types.nullOr types.port;
+ default = null;
+ description = "Override the external port of the onion service.";
+ };
+ };
+ }
+ ));
+ };
+
+ config = mkMerge [
+ (mkIf (cfg != {}) {
+ # Define hidden services
+ services.tor = {
+ enable = true;
+ hiddenServices = genAttrs activeServices (name:
+ 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;
+ }
+ );
+ };
+
+ # Enable public services to access their own onion addresses
+ nix-bitcoin.onionAddresses.access = (
+ genAttrs publicServices singleton
+ ) // {
+ # Allow the operator user to access onion addresses for all active services
+ ${config.nix-bitcoin.operator.name} = mkIf config.nix-bitcoin.operator.enable activeServices;
+ };
+ systemd.services = let
+ onionAddresses = [ "onion-addresses.service" ];
+ in genAttrs publicServices (service: {
+ requires = onionAddresses;
+ after = onionAddresses;
+ });
+ })
+
+ # Set getPublicAddressCmd for public services
+ {
+ services = let
+ # publicServices' doesn't depend on config.services.*.enable,
+ # so we can use it to define config.services without causing infinite recursion
+ publicServices' = builtins.filter (service:
+ let srv = cfg.${service};
+ in srv.public && srv.enable
+ ) services;
+ in genAttrs publicServices' (service: {
+ getPublicAddressCmd = "cat ${config.nix-bitcoin.onionAddresses.dataDir}/${service}/${service}";
+ });
+ }
+
+ # Set sensible defaults for some services
+ {
+ nix-bitcoin.onionServices = {
+ spark-wallet = {
+ externalPort = 80;
+ # Enable 'public' by default, but don't auto-enable the onion service.
+ # When the onion service is enabled, 'public' lets spark-wallet generate
+ # a QR code for accessing the web interface.
+ public = true;
+ # Low priority so we can override this with mkDefault in ./presets/enable-tor.nix
+ enable = mkOverride 1400 false;
+ };
+ btcpayserver = {
+ externalPort = 80;
+ };
+ };
+ }
+ ];
+}
diff --git a/modules/presets/enable-tor.nix b/modules/presets/enable-tor.nix
new file mode 100644
index 00000000..8d16a9e3
--- /dev/null
+++ b/modules/presets/enable-tor.nix
@@ -0,0 +1,32 @@
+{ lib, ... }:
+let
+ defaultTrue = lib.mkDefault true;
+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;
+ # disable Tor enforcement until btcpayserver can fetch rates over Tor
+ # btcpayserver.enforceTor = true;
+ nbxplorer.enforceTor = true;
+ spark-wallet.enforceTor = true;
+ recurring-donations.enforceTor = true;
+ };
+
+ # Add onion services for incoming connections
+ nix-bitcoin.onionServices = {
+ bitcoind.enable = defaultTrue;
+ liquidd.enable = defaultTrue;
+ electrs.enable = defaultTrue;
+ spark-wallet.enable = defaultTrue;
+ };
+}
diff --git a/modules/presets/secure-node.nix b/modules/presets/secure-node.nix
index c4f06fa0..a0472c47 100644
--- a/modules/presets/secure-node.nix
+++ b/modules/presets/secure-node.nix
@@ -14,23 +14,9 @@ let
in {
imports = [
../modules.nix
- ../nodeinfo.nix
- ../nix-bitcoin-webindex.nix
+ ./enable-tor.nix
];
- options = {
- services.clightning.onionport = mkOption {
- type = types.port;
- default = 9735;
- description = "Port on which to listen for tor client connections.";
- };
- services.lnd.onionport = mkOption {
- type = types.ints.u16;
- default = 9735;
- description = "Port on which to listen for tor client connections.";
- };
- };
-
config = {
# For backwards compatibility only
nix-bitcoin.secretsDir = mkDefault "/secrets";
@@ -39,99 +25,36 @@ in {
nix-bitcoin.security.hideProcessInformation = true;
- # Tor
- services.tor = {
- enable = true;
- client.enable = true;
+ environment.systemPackages = with pkgs; [
+ jq
+ ];
- hiddenServices.sshd = mkHiddenService { port = 22; };
- };
+ # sshd
+ services.tor.hiddenServices.sshd = mkHiddenService { port = 22; };
+ nix-bitcoin.onionAddresses.access.${operatorName} = [ "sshd" ];
- # bitcoind
services.bitcoind = {
enable = true;
listen = true;
dataDirReadableByGroup = mkIf cfg.electrs.high-memory true;
- enforceTor = true;
- port = 8333;
assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6";
addnodes = [ "ecoc5q34tmbq54wl.onion" ];
discover = false;
addresstype = "bech32";
dbCache = 1000;
- # higher rpcthread count due to reports that lightning implementations fail
- # under high bitcoind rpc load
- rpcthreads = 16;
};
- services.tor.hiddenServices.bitcoind = mkHiddenService { port = cfg.bitcoind.port; toHost = cfg.bitcoind.bind; };
-
- # clightning
- services.clightning.enforceTor = true;
- services.tor.hiddenServices.clightning = mkIf cfg.clightning.enable (mkHiddenService {
- port = cfg.clightning.onionport;
- toHost = cfg.clightning.bind-addr;
- toPort = cfg.clightning.bindport;
- });
- # lnd
- services.lnd.enforceTor = true;
- services.tor.hiddenServices.lnd = mkIf cfg.lnd.enable (mkHiddenService { port = cfg.lnd.onionport; toHost = cfg.lnd.listen; toPort = cfg.lnd.listenPort; });
-
- # lightning-loop
- services.lightning-loop.enforceTor = true;
-
- # liquidd
services.liquidd = {
- rpcuser = "liquidrpc";
prune = 1000;
validatepegin = true;
listen = true;
- enforceTor = true;
- port = 7042;
- };
- services.tor.hiddenServices.liquidd = mkIf cfg.liquidd.enable (mkHiddenService { port = cfg.liquidd.port; toHost = cfg.liquidd.bind; });
-
- # electrs
- services.electrs = {
- port = 50001;
- enforceTor = true;
- };
- services.tor.hiddenServices.electrs = mkIf cfg.electrs.enable (mkHiddenService {
- port = cfg.electrs.port; toHost = cfg.electrs.address;
- });
-
- # btcpayserver
- # disable tor enforcement until btcpayserver can fetch rates over Tor
- services.btcpayserver.enforceTor = false;
- services.nbxplorer.enforceTor = true;
- services.tor.hiddenServices.btcpayserver = mkIf cfg.btcpayserver.enable (mkHiddenService { port = 80; toPort = 23000; toHost = cfg.btcpayserver.bind; });
-
- services.spark-wallet = {
- onion-service = true;
- enforceTor = true;
};
- services.recurring-donations.enforceTor = true;
-
- services.nix-bitcoin-webindex.enforceTor = true;
-
- # Backups
- services.backups = {
- program = "duplicity";
- frequency = "daily";
- };
+ nix-bitcoin.nodeinfo.enable = true;
- environment.systemPackages = with pkgs; [
- tor
- jq
- qrencode
- ];
-
- services.onion-chef = {
- enable = true;
- access.${operatorName} = [ "bitcoind" "clightning" "nginx" "liquidd" "spark-wallet" "electrs" "btcpayserver" "sshd" ];
- };
+ services.backups.frequency = "daily";
+ # operator
nix-bitcoin.operator.enable = true;
users.users.${operatorName} = {
openssh.authorizedKeys.keys = config.users.users.root.openssh.authorizedKeys.keys;
diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix
index 6f9ca684..a130dd2f 100644
--- a/modules/spark-wallet.nix
+++ b/modules/spark-wallet.nix
@@ -5,18 +5,17 @@ with lib;
let
cfg = config.services.spark-wallet;
inherit (config) nix-bitcoin-services;
- onion-chef-service = (if cfg.onion-service then [ "onion-chef.service" ] else []);
# Use wasabi rate provider because the default (bitstamp) doesn't accept
# connections through Tor
torRateProvider = "--rate-provider wasabi --proxy socks5h://${config.services.tor.client.socksListenAddress}";
startScript = ''
- ${optionalString cfg.onion-service ''
- publicURL="--public-url http://$(cat /var/lib/onion-chef/spark-wallet/spark-wallet)"
+ ${optionalString (cfg.getPublicAddressCmd != "") ''
+ publicURL="--public-url http://$(${cfg.getPublicAddressCmd})"
''}
exec ${config.nix-bitcoin.pkgs.spark-wallet}/bin/spark-wallet \
--ln-path '${config.services.clightning.networkDir}' \
- --host ${cfg.host} \
+ --host ${cfg.address} --port ${toString cfg.port} \
--config '${config.nix-bitcoin.secretsDir}/spark-wallet-login' \
${optionalString cfg.enforceTor torRateProvider} \
$publicURL \
@@ -31,24 +30,31 @@ in {
If enabled, the spark-wallet service will be installed.
'';
};
- host = mkOption {
+ address = mkOption {
type = types.str;
default = "localhost";
- description = "http(s) server listen address.";
+ description = "http(s) server address.";
};
- onion-service = mkOption {
- type = types.bool;
- default = false;
- description = ''
- "If enabled, configures spark-wallet to be reachable through an onion service.";
- '';
+ port = mkOption {
+ type = types.port;
+ default = 9737;
+ description = "http(s) server port.";
};
extraArgs = mkOption {
type = types.separatedString " ";
default = "";
description = "Extra command line arguments passed to spark-wallet.";
};
- enforceTor = nix-bitcoin-services.enforceTor;
+ getPublicAddressCmd = mkOption {
+ type = types.str;
+ default = "";
+ description = ''
+ Bash expression which outputs the public service address.
+ If set, spark-wallet prints a QR code to the systemd journal which
+ encodes an URL for accessing the web interface.
+ '';
+ };
+ inherit (nix-bitcoin-services) enforceTor;
};
config = mkIf cfg.enable {
@@ -61,25 +67,16 @@ in {
};
users.groups.spark-wallet = {};
- services.tor.hiddenServices.spark-wallet = mkIf cfg.onion-service {
- map = [{
- port = 80; toPort = 9737; toHost = cfg.host;
- }];
- version = 3;
- };
- services.onion-chef.enable = cfg.onion-service;
- services.onion-chef.access.spark-wallet = if cfg.onion-service then [ "spark-wallet" ] else [];
systemd.services.spark-wallet = {
description = "Run spark-wallet";
wantedBy = [ "multi-user.target" ];
- requires = [ "clightning.service" ] ++ onion-chef-service;
- after = [ "clightning.service" ] ++ onion-chef-service;
+ requires = [ "clightning.service" ];
+ after = [ "clightning.service" ];
script = startScript;
serviceConfig = nix-bitcoin-services.defaultHardening // {
User = "spark-wallet";
Restart = "on-failure";
RestartSec = "10s";
- ReadWritePaths = mkIf cfg.onion-service "/var/lib/onion-chef";
} // (if cfg.enforceTor
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP)
diff --git a/modules/versioning.nix b/modules/versioning.nix
index ff447280..f06a593d 100644
--- a/modules/versioning.nix
+++ b/modules/versioning.nix
@@ -5,7 +5,19 @@ let
version = config.nix-bitcoin.configVersion;
# Sorted by increasing version numbers
- changes = [
+ changes = let
+ mkOnionServiceChange = service: {
+ version = "0.0.30";
+ condition = config.services.${service}.enable;
+ message = ''
+ The onion service for ${service} has been disabled in the default
+ configuration (`secure-node.nix`).
+
+ To enable the onion service, add the following to your configuration:
+ nix-bitcon.onionServices.${service}.enable = true;
+ '';
+ };
+ in [
{
version = "0.0.26";
condition = config.services.joinmarket.enable;
@@ -54,6 +66,9 @@ let
https://github.com/JoinMarket-Org/joinmarket-clientserver/blob/v0.8.0/docs/NATIVE-SEGWIT-UPGRADE.md
'';
}
+ (mkOnionServiceChange "clightning")
+ (mkOnionServiceChange "lnd")
+ (mkOnionServiceChange "btcpayserver")
];
incompatibleChanges = optionals
@@ -76,6 +91,10 @@ let
lastChange = builtins.elemAt changes (builtins.length changes - 1);
in
{
+ imports = [
+ ./obsolete-options.nix
+ ];
+
options = {
nix-bitcoin.configVersion = mkOption {
type = with types; nullOr str;
@@ -93,6 +112,6 @@ in
config = {
# Force evaluation. An actual option value is never assigned
- system.extraDependencies = optional (builtins.length incompatibleChanges > 0) (builtins.throw errorMsg);
+ system = optionalAttrs (builtins.length incompatibleChanges > 0) (builtins.throw errorMsg);
};
}
diff --git a/pkgs/default.nix b/pkgs/default.nix
index 639b6b2f..db178c1e 100644
--- a/pkgs/default.nix
+++ b/pkgs/default.nix
@@ -20,7 +20,5 @@ let self = {
pinned = import ./pinned.nix;
- lib = import ./lib.nix { inherit (pkgs) lib; };
-
modulesPkgs = self // self.pinned;
}; in self
diff --git a/pkgs/lib.nix b/pkgs/lib.nix
deleted file mode 100644
index 43dbe450..00000000
--- a/pkgs/lib.nix
+++ /dev/null
@@ -1,5 +0,0 @@
-{ lib }:
-{
- # An address type that checks that there's no port
- ipv4Address = lib.types.addCheck lib.types.str (s: builtins.length (builtins.split ":" s) == 1);
-}
diff --git a/test/tests.nix b/test/tests.nix
index 10c608de..83e46296 100644
--- a/test/tests.nix
+++ b/test/tests.nix
@@ -44,7 +44,7 @@ let testEnv = rec {
tests.spark-wallet = cfg.spark-wallet.enable;
tests.lnd = cfg.lnd.enable;
- services.lnd.listenPort = 9736;
+ services.lnd.port = 9736;
tests.lightning-loop = cfg.lightning-loop.enable;
@@ -68,6 +68,8 @@ let testEnv = rec {
'';
};
+ tests.nodeinfo = config.nix-bitcoin.nodeinfo.enable;
+
tests.backups = cfg.backups.enable;
# To test that unused secrets are made inaccessible by 'setup-secrets'
@@ -119,6 +121,8 @@ let testEnv = rec {
services.joinmarket.enable = true;
services.backups.enable = true;
+ nix-bitcoin.nodeinfo.enable = true;
+
services.hardware-wallets = {
trezor = true;
ledger = true;
@@ -130,7 +134,6 @@ let testEnv = rec {
scenarios.full
../modules/presets/secure-node.nix
];
- services.nix-bitcoin-webindex.enable = true;
tests.secure-node = true;
tests.banlist-and-restart = true;
diff --git a/test/tests.py b/test/tests.py
index 53165ed8..329dcc6f 100644
--- a/test/tests.py
+++ b/test/tests.py
@@ -216,17 +216,19 @@ def _():
)
+@test("nodeinfo")
+def _():
+ status, _ = machine.execute("systemctl is-enabled --quiet onion-addresses 2> /dev/null")
+ if status == 0:
+ machine.wait_for_unit("onion-addresses")
+ json_info = succeed("sudo -u operator nodeinfo")
+ info = json.loads(json_info)
+ assert info["bitcoind"]["local_address"]
+
+
@test("secure-node")
def _():
- assert_running("onion-chef")
-
- # FIXME: use 'wait_for_unit' because 'create-web-index' always fails during startup due
- # to incomplete unit dependencies.
- # 'create-web-index' implicitly tests 'nodeinfo'.
- machine.wait_for_unit("create-web-index")
- assert_running("nginx")
- wait_for_open_port(ip("nginx"), 80)
- assert_matches(f"curl {ip('nginx')}", "nix-bitcoin")
+ assert_running("onion-addresses")
# Run this test before the following tests that shut down services