improve nodeinfo
- enable usage outside of secure-node.nix - use json as the output format - show ports - also show local addresses, which is particularly useful when netns-isolation is enabled - only show enabled services
This commit is contained in:
parent
f6b883a9ac
commit
323a431aba
@ -49,7 +49,6 @@ Features
|
|||||||
---
|
---
|
||||||
A [configuration preset](modules/presets/secure-node.nix) for setting up a secure node
|
A [configuration preset](modules/presets/secure-node.nix) for setting up a secure node
|
||||||
* All applications use Tor for outbound connections and support accepting inbound connections via onion services.
|
* All applications use Tor for outbound connections and support accepting inbound connections via onion services.
|
||||||
* Includes a [nodeinfo](modules/nodeinfo.nix) script which prints basic info about the node.
|
|
||||||
|
|
||||||
NixOS modules
|
NixOS modules
|
||||||
* Application services
|
* Application services
|
||||||
@ -74,6 +73,7 @@ NixOS modules
|
|||||||
* [bitcoin-core-hwi](https://github.com/bitcoin-core/HWI)
|
* [bitcoin-core-hwi](https://github.com/bitcoin-core/HWI)
|
||||||
* Helper
|
* Helper
|
||||||
* [netns-isolation](modules/netns-isolation.nix): isolates applications on the network-level via network namespaces
|
* [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
|
* [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`)
|
* [operator](modules/operator.nix): adds non-root user `operator` who has access to client tools (e.g. `bitcoin-cli`, `lightning-cli`)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ fetch-release > nix-bitcoin-release.nix
|
|||||||
|
|
||||||
Nodeinfo
|
Nodeinfo
|
||||||
---
|
---
|
||||||
Run `nodeinfo` to see the onion addresses for enabled services.
|
Run `nodeinfo` to see onion addresses and local addresses for enabled services.
|
||||||
|
|
||||||
Connect to spark-wallet
|
Connect to spark-wallet
|
||||||
---
|
---
|
||||||
@ -86,10 +86,10 @@ Connect to electrs
|
|||||||
nixops deploy -d bitcoin-node
|
nixops deploy -d bitcoin-node
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Get electrs onion address
|
3. Get electrs onion address with format `<onion-address>:<port>`
|
||||||
|
|
||||||
```
|
```
|
||||||
nodeinfo | grep 'ELECTRS_ONION'
|
nodeinfo | jq -r .electrs.onion_address
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Connect to electrs
|
4. Connect to electrs
|
||||||
@ -98,7 +98,7 @@ Connect to electrs
|
|||||||
|
|
||||||
On Desktop
|
On Desktop
|
||||||
```
|
```
|
||||||
electrum --oneserver -1 -s "<ELECTRS_ONION>:50001:t" -p socks5:localhost:9050
|
electrum --oneserver -1 -s "<electrs onion address>:t" -p socks5:localhost:9050
|
||||||
```
|
```
|
||||||
|
|
||||||
On Android
|
On Android
|
||||||
@ -107,16 +107,16 @@ Connect to electrs
|
|||||||
Network > Proxy mode: socks5, Host: 127.0.0.1, Port: 9050
|
Network > Proxy mode: socks5, Host: 127.0.0.1, Port: 9050
|
||||||
Network > Auto-connect: OFF
|
Network > Auto-connect: OFF
|
||||||
Network > One-server mode: ON
|
Network > One-server mode: ON
|
||||||
Network > Server: <ELECTRS_ONION>:50001:t
|
Network > Server: <electrs onion address>: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
|
nixops ssh operator@bitcoin-node
|
||||||
nodeinfo | grep 'SSHD_ONION'
|
nodeinfo | jq -r .sshd.onion_address | sed 's/:.*//'
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Create a SSH key
|
2. Create a SSH key
|
||||||
@ -131,14 +131,14 @@ Connect to nix-bitcoin node through ssh Tor Hidden Service
|
|||||||
# FIXME: Add your SSH pubkey
|
# FIXME: Add your SSH pubkey
|
||||||
services.openssh.enable = true;
|
services.openssh.enable = true;
|
||||||
users.users.root = {
|
users.users.root = {
|
||||||
openssh.authorizedKeys.keys = [ "[contents of ~/.ssh/id_ed25519.pub]" ];
|
openssh.authorizedKeys.keys = [ "<contents of ~/.ssh/id_ed25519.pub>" ];
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
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 <random port of your choosing>:localhost:22 root@<SSH onion address>
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Edit your `network-nixos.nix` to look like this
|
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 =
|
bitcoin-node =
|
||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{ deployment.targetHost = "127.0.0.1";
|
{ deployment.targetHost = "127.0.0.1";
|
||||||
deployment.targetPort = [random port of your choosing];
|
deployment.targetPort = <random port of your choosing>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
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
|
## Run the tumbler
|
||||||
|
|
||||||
The tumbler needs to be able to run in the background for a long time, use screen
|
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
|
1. Add screen to your `environment.systemPackages`, for example
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ with lib;
|
|||||||
./onion-addresses.nix
|
./onion-addresses.nix
|
||||||
./onion-services.nix
|
./onion-services.nix
|
||||||
./netns-isolation.nix
|
./netns-isolation.nix
|
||||||
|
./nodeinfo.nix
|
||||||
./backups.nix
|
./backups.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -1,74 +1,117 @@
|
|||||||
{ config, lib, pkgs, ... }:
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
with lib;
|
with lib;
|
||||||
|
|
||||||
let
|
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" ''
|
script = pkgs.writeScriptBin "nodeinfo" ''
|
||||||
set -eo pipefail
|
#!${pkgs.python3}/bin/python
|
||||||
|
|
||||||
BITCOIND_ONION="$(cat /var/lib/onion-addresses/${operatorName}/bitcoind)"
|
import json
|
||||||
echo BITCOIND_ONION="$BITCOIND_ONION"
|
import subprocess
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
if systemctl is-active --quiet clightning; then
|
def success(*args):
|
||||||
CLIGHTNING_NODEID=$(lightning-cli getinfo | jq -r '.id')
|
return subprocess.call(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
|
||||||
CLIGHTNING_ONION="$(cat /var/lib/onion-addresses/${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
|
def is_active(unit):
|
||||||
LND_NODEID=$(lncli getinfo | jq -r '.uris[0]')
|
return success("systemctl", "is-active", "--quiet", unit)
|
||||||
echo LND_NODEID="$LND_NODEID"
|
|
||||||
fi
|
|
||||||
|
|
||||||
NGINX_ONION_FILE=/var/lib/onion-addresses/${operatorName}/nginx
|
def is_enabled(unit):
|
||||||
if [ -e "$NGINX_ONION_FILE" ]; then
|
return success("systemctl", "is-enabled", "--quiet", unit)
|
||||||
NGINX_ONION="$(cat $NGINX_ONION_FILE)"
|
|
||||||
echo NGINX_ONION="$NGINX_ONION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
LIQUIDD_ONION_FILE=/var/lib/onion-addresses/${operatorName}/liquidd
|
def cmd(*args):
|
||||||
if [ -e "$LIQUIDD_ONION_FILE" ]; then
|
return subprocess.run(args, stdout=subprocess.PIPE).stdout.decode('utf-8')
|
||||||
LIQUIDD_ONION="$(cat $LIQUIDD_ONION_FILE)"
|
|
||||||
echo LIQUIDD_ONION="$LIQUIDD_ONION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
SPARKWALLET_ONION_FILE=/var/lib/onion-addresses/${operatorName}/spark-wallet
|
def shell(*args):
|
||||||
if [ -e "$SPARKWALLET_ONION_FILE" ]; then
|
return cmd("bash", "-c", *args).strip()
|
||||||
SPARKWALLET_ONION="$(cat $SPARKWALLET_ONION_FILE)"
|
|
||||||
echo SPARKWALLET_ONION="http://$SPARKWALLET_ONION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
ELECTRS_ONION_FILE=/var/lib/onion-addresses/${operatorName}/electrs
|
infos = OrderedDict()
|
||||||
if [ -e "$ELECTRS_ONION_FILE" ]; then
|
operator = "${config.nix-bitcoin.operator.name}"
|
||||||
ELECTRS_ONION="$(cat $ELECTRS_ONION_FILE)"
|
|
||||||
echo ELECTRS_ONION="$ELECTRS_ONION"
|
|
||||||
fi
|
|
||||||
|
|
||||||
BTCPAYSERVER_ONION_FILE=/var/lib/onion-addresses/${operatorName}/btcpayserver
|
def set_onion_address(info, name, port):
|
||||||
if [ -e "$BTCPAYSERVER_ONION_FILE" ]; then
|
path = f"/var/lib/onion-addresses/{operator}/{name}"
|
||||||
BTCPAYSERVER_ONION="$(cat $BTCPAYSERVER_ONION_FILE)"
|
try:
|
||||||
echo BTCPAYSERVER_ONION="$BTCPAYSERVER_ONION"
|
with open(path, "r") as f:
|
||||||
fi
|
onion_address = f.read().strip()
|
||||||
|
except OSError:
|
||||||
|
print(f"error reading file {path}", file=sys.stderr)
|
||||||
|
return
|
||||||
|
info["onion_address"] = f"{onion_address}:{port}"
|
||||||
|
|
||||||
SSHD_ONION_FILE=/var/lib/onion-addresses/${operatorName}/sshd
|
def add_service(service, make_info):
|
||||||
if [ -e "$SSHD_ONION_FILE" ]; then
|
if not is_active(service):
|
||||||
SSHD_ONION="$(cat $SSHD_ONION_FILE)"
|
infos[service] = "service is not running"
|
||||||
echo SSHD_ONION="$SSHD_ONION"
|
else:
|
||||||
fi
|
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 {
|
in {
|
||||||
options = {
|
options = {
|
||||||
programs.nodeinfo = mkOption {
|
nix-bitcoin.nodeinfo = {
|
||||||
readOnly = true;
|
enable = mkEnableOption "nodeinfo";
|
||||||
default = script;
|
program = mkOption {
|
||||||
|
readOnly = true;
|
||||||
|
default = script;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
environment.systemPackages = [ script ];
|
environment.systemPackages = optional cfg.enable script;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,6 @@ let
|
|||||||
in {
|
in {
|
||||||
imports = [
|
imports = [
|
||||||
../modules.nix
|
../modules.nix
|
||||||
../nodeinfo.nix
|
|
||||||
./enable-tor.nix
|
./enable-tor.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -75,5 +74,7 @@ in {
|
|||||||
cp "${config.users.users.root.home}/.vbox-nixops-client-key" "${config.users.users.${operatorName}.home}"
|
cp "${config.users.users.root.home}/.vbox-nixops-client-key" "${config.users.users.${operatorName}.home}"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
nix-bitcoin.nodeinfo.enable = true;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -68,6 +68,8 @@ let testEnv = rec {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
tests.nodeinfo = config.nix-bitcoin.nodeinfo.enable;
|
||||||
|
|
||||||
tests.backups = cfg.backups.enable;
|
tests.backups = cfg.backups.enable;
|
||||||
|
|
||||||
# To test that unused secrets are made inaccessible by 'setup-secrets'
|
# To test that unused secrets are made inaccessible by 'setup-secrets'
|
||||||
@ -119,6 +121,8 @@ let testEnv = rec {
|
|||||||
services.joinmarket.enable = true;
|
services.joinmarket.enable = true;
|
||||||
services.backups.enable = true;
|
services.backups.enable = true;
|
||||||
|
|
||||||
|
nix-bitcoin.nodeinfo.enable = true;
|
||||||
|
|
||||||
services.hardware-wallets = {
|
services.hardware-wallets = {
|
||||||
trezor = true;
|
trezor = true;
|
||||||
ledger = true;
|
ledger = true;
|
||||||
|
@ -216,6 +216,16 @@ 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")
|
@test("secure-node")
|
||||||
def _():
|
def _():
|
||||||
assert_running("onion-addresses")
|
assert_running("onion-addresses")
|
||||||
|
Loading…
Reference in New Issue
Block a user