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
|
||||
* 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
|
||||
* Application services
|
||||
@ -74,6 +73,7 @@ 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`)
|
||||
|
||||
|
@ -8,7 +8,7 @@ fetch-release > nix-bitcoin-release.nix
|
||||
|
||||
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
|
||||
---
|
||||
@ -86,10 +86,10 @@ Connect to electrs
|
||||
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
|
||||
@ -98,7 +98,7 @@ Connect to electrs
|
||||
|
||||
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
|
||||
@ -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: <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
|
||||
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 = [ "<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
|
||||
@ -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 = <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
|
||||
---
|
||||
@ -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
|
||||
|
||||
|
@ -27,6 +27,7 @@ with lib;
|
||||
./onion-addresses.nix
|
||||
./onion-services.nix
|
||||
./netns-isolation.nix
|
||||
./nodeinfo.nix
|
||||
./backups.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
|
||||
#!${pkgs.python3}/bin/python
|
||||
|
||||
BITCOIND_ONION="$(cat /var/lib/onion-addresses/${operatorName}/bitcoind)"
|
||||
echo BITCOIND_ONION="$BITCOIND_ONION"
|
||||
import json
|
||||
import subprocess
|
||||
from collections import OrderedDict
|
||||
|
||||
if systemctl is-active --quiet clightning; then
|
||||
CLIGHTNING_NODEID=$(lightning-cli getinfo | jq -r '.id')
|
||||
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
|
||||
def success(*args):
|
||||
return subprocess.call(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0
|
||||
|
||||
if systemctl is-active --quiet lnd; then
|
||||
LND_NODEID=$(lncli getinfo | jq -r '.uris[0]')
|
||||
echo LND_NODEID="$LND_NODEID"
|
||||
fi
|
||||
def is_active(unit):
|
||||
return success("systemctl", "is-active", "--quiet", unit)
|
||||
|
||||
NGINX_ONION_FILE=/var/lib/onion-addresses/${operatorName}/nginx
|
||||
if [ -e "$NGINX_ONION_FILE" ]; then
|
||||
NGINX_ONION="$(cat $NGINX_ONION_FILE)"
|
||||
echo NGINX_ONION="$NGINX_ONION"
|
||||
fi
|
||||
def is_enabled(unit):
|
||||
return success("systemctl", "is-enabled", "--quiet", unit)
|
||||
|
||||
LIQUIDD_ONION_FILE=/var/lib/onion-addresses/${operatorName}/liquidd
|
||||
if [ -e "$LIQUIDD_ONION_FILE" ]; then
|
||||
LIQUIDD_ONION="$(cat $LIQUIDD_ONION_FILE)"
|
||||
echo LIQUIDD_ONION="$LIQUIDD_ONION"
|
||||
fi
|
||||
def cmd(*args):
|
||||
return subprocess.run(args, stdout=subprocess.PIPE).stdout.decode('utf-8')
|
||||
|
||||
SPARKWALLET_ONION_FILE=/var/lib/onion-addresses/${operatorName}/spark-wallet
|
||||
if [ -e "$SPARKWALLET_ONION_FILE" ]; then
|
||||
SPARKWALLET_ONION="$(cat $SPARKWALLET_ONION_FILE)"
|
||||
echo SPARKWALLET_ONION="http://$SPARKWALLET_ONION"
|
||||
fi
|
||||
def shell(*args):
|
||||
return cmd("bash", "-c", *args).strip()
|
||||
|
||||
ELECTRS_ONION_FILE=/var/lib/onion-addresses/${operatorName}/electrs
|
||||
if [ -e "$ELECTRS_ONION_FILE" ]; then
|
||||
ELECTRS_ONION="$(cat $ELECTRS_ONION_FILE)"
|
||||
echo ELECTRS_ONION="$ELECTRS_ONION"
|
||||
fi
|
||||
infos = OrderedDict()
|
||||
operator = "${config.nix-bitcoin.operator.name}"
|
||||
|
||||
BTCPAYSERVER_ONION_FILE=/var/lib/onion-addresses/${operatorName}/btcpayserver
|
||||
if [ -e "$BTCPAYSERVER_ONION_FILE" ]; then
|
||||
BTCPAYSERVER_ONION="$(cat $BTCPAYSERVER_ONION_FILE)"
|
||||
echo BTCPAYSERVER_ONION="$BTCPAYSERVER_ONION"
|
||||
fi
|
||||
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}"
|
||||
|
||||
SSHD_ONION_FILE=/var/lib/onion-addresses/${operatorName}/sshd
|
||||
if [ -e "$SSHD_ONION_FILE" ]; then
|
||||
SSHD_ONION="$(cat $SSHD_ONION_FILE)"
|
||||
echo SSHD_ONION="$SSHD_ONION"
|
||||
fi
|
||||
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;
|
||||
};
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ let
|
||||
in {
|
||||
imports = [
|
||||
../modules.nix
|
||||
../nodeinfo.nix
|
||||
./enable-tor.nix
|
||||
];
|
||||
|
||||
@ -75,5 +74,7 @@ in {
|
||||
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;
|
||||
|
||||
# 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;
|
||||
|
@ -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")
|
||||
def _():
|
||||
assert_running("onion-addresses")
|
||||
|
Loading…
Reference in New Issue
Block a user