Merge #255: Improve netns-isolation and Tor config
b4b607dfa5
netns: simplify firewall setup (Erik Arvstedt)25639cec42
netns: fix error msg when starting netns (Erik Arvstedt)67068afd6b
netns: fix error when stopping netns (Erik Arvstedt)4ff88efc50
netns: add address binding test (Erik Arvstedt)8da01fe8a6
lightning-loop: allow RPC access from main netns (Erik Arvstedt)d76b080b74
lightning-loop: add RPC and REST server options (Erik Arvstedt)9ddf7864a4
lightning-loop regtest: fix incorrectly succeeding test (Erik Arvstedt)e66636ef0e
liquidd: use type str for rpcbind (Erik Arvstedt)de23fdd377
lnd: use type str for rpclisten, restlisten (Erik Arvstedt)8b053326cc
bitcoind: use type str for rpcbind (Erik Arvstedt)6903e8afcc
netns-liquidd: allow RPC access from main netns (Erik Arvstedt)82f4901880
netns-lnd: allow RPC access from main netns (Erik Arvstedt)58d24e735d
netns-bitcoind: allow RPC access from main netns (Erik Arvstedt)0e2ff948d3
test: add scenario 'netnsRegtest' (Erik Arvstedt)e0675cb256
move enforceTor logic to service modules (Erik Arvstedt)0cc8caa737
lnd: only set tor.active on enforceTor (Erik Arvstedt)9a931483b9
netns test: remove strict dependency on clightning, electrs (Erik Arvstedt)bae1b7f413
netns test: improve ping test (Erik Arvstedt)5e0e16529c
netns: fix default addressblock value type (Erik Arvstedt) Pull request description: ACKs for top commit: jonasnick: ACKb4b607dfa5
nixbitcoin: ACKb4b607dfa5
Tree-SHA512: b290831d9a3fa4de56b0f19cf84a1998e830aa844532d7cba8cd8227c785a23bfa1514123a974652e8e61060e1297b6bfbcff9640580206a04c5292309b1daef
This commit is contained in:
commit
dbad828851
@ -16,7 +16,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
- TestModules=1 STABLE=1 SCENARIO=default
|
- TestModules=1 STABLE=1 SCENARIO=default
|
||||||
- TestModules=1 STABLE=1 SCENARIO=netns
|
- TestModules=1 STABLE=1 SCENARIO=netns
|
||||||
- EvalModules=1 STABLE=1
|
- TestModules=1 STABLE=1 SCENARIO=netnsRegtest
|
||||||
- PKG=hwi STABLE=1
|
- PKG=hwi STABLE=1
|
||||||
- PKG=hwi STABLE=0
|
- PKG=hwi STABLE=0
|
||||||
- PKG=lightning-charge STABLE=1
|
- PKG=lightning-charge STABLE=1
|
||||||
@ -35,12 +35,6 @@ env:
|
|||||||
- PKG=joinmarket STABLE=0
|
- PKG=joinmarket STABLE=0
|
||||||
script:
|
script:
|
||||||
- printf '%s (%s)\n' "$NIX_PATH" "$VER"
|
- printf '%s (%s)\n' "$NIX_PATH" "$VER"
|
||||||
#
|
|
||||||
- |
|
|
||||||
if [[ $EvalModules ]]; then
|
|
||||||
test/run-tests.sh --scenario full eval
|
|
||||||
travis_terminate 0
|
|
||||||
fi
|
|
||||||
- |
|
- |
|
||||||
getBuildExpr() {
|
getBuildExpr() {
|
||||||
if [[ $TestModules ]]; then
|
if [[ $TestModules ]]; then
|
||||||
|
@ -39,7 +39,8 @@ let
|
|||||||
"rpcwhitelist=${user.name}:${lib.strings.concatStringsSep "," user.rpcwhitelist}"}
|
"rpcwhitelist=${user.name}:${lib.strings.concatStringsSep "," user.rpcwhitelist}"}
|
||||||
'') (builtins.attrValues cfg.rpc.users)
|
'') (builtins.attrValues cfg.rpc.users)
|
||||||
}
|
}
|
||||||
${lib.concatMapStrings (rpcbind: "rpcbind=${rpcbind}\n") cfg.rpcbind}
|
rpcbind=${cfg.rpcbind}
|
||||||
|
rpcconnect=${cfg.rpcbind}
|
||||||
${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
|
${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
|
||||||
|
|
||||||
# Wallet options
|
# Wallet options
|
||||||
@ -149,8 +150,8 @@ in {
|
|||||||
description = "Set the number of threads to service RPC calls";
|
description = "Set the number of threads to service RPC calls";
|
||||||
};
|
};
|
||||||
rpcbind = mkOption {
|
rpcbind = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.str;
|
||||||
default = [ "127.0.0.1" ];
|
default = "127.0.0.1";
|
||||||
description = ''
|
description = ''
|
||||||
Bind to given address to listen for JSON-RPC connections.
|
Bind to given address to listen for JSON-RPC connections.
|
||||||
'';
|
'';
|
||||||
@ -182,7 +183,7 @@ in {
|
|||||||
};
|
};
|
||||||
proxy = mkOption {
|
proxy = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
|
||||||
description = "Connect through SOCKS5 proxy";
|
description = "Connect through SOCKS5 proxy";
|
||||||
};
|
};
|
||||||
listen = mkOption {
|
listen = mkOption {
|
||||||
@ -275,17 +276,12 @@ in {
|
|||||||
description = "What type of addresses to use";
|
description = "What type of addresses to use";
|
||||||
};
|
};
|
||||||
cli = mkOption {
|
cli = mkOption {
|
||||||
type = types.package;
|
|
||||||
# Overriden on netns-isolation
|
|
||||||
default = cfg.cliBase;
|
|
||||||
description = "Binary to connect with the bitcoind instance.";
|
|
||||||
};
|
|
||||||
cliBase = mkOption {
|
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
type = types.package;
|
type = types.package;
|
||||||
default = pkgs.writeScriptBin "bitcoin-cli" ''
|
default = pkgs.writeScriptBin "bitcoin-cli" ''
|
||||||
exec ${cfg.package}/bin/bitcoin-cli -datadir='${cfg.dataDir}' "$@"
|
exec ${cfg.package}/bin/bitcoin-cli -datadir='${cfg.dataDir}' "$@"
|
||||||
'';
|
'';
|
||||||
|
description = "Binary to connect with the bitcoind instance.";
|
||||||
};
|
};
|
||||||
enforceTor = nix-bitcoin-services.enforceTor;
|
enforceTor = nix-bitcoin-services.enforceTor;
|
||||||
};
|
};
|
||||||
@ -341,9 +337,8 @@ in {
|
|||||||
fi
|
fi
|
||||||
'';
|
'';
|
||||||
postStart = ''
|
postStart = ''
|
||||||
cd ${cfg.cliBase}/bin
|
|
||||||
# Poll until bitcoind accepts commands. This can take a long time.
|
# Poll until bitcoind accepts commands. This can take a long time.
|
||||||
while ! ./bitcoin-cli getnetworkinfo &> /dev/null; do
|
while ! ${cfg.cli}/bin/bitcoin-cli getnetworkinfo &> /dev/null; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
'';
|
'';
|
||||||
@ -368,7 +363,7 @@ in {
|
|||||||
bindsTo = [ "bitcoind.service" ];
|
bindsTo = [ "bitcoind.service" ];
|
||||||
after = [ "bitcoind.service" ];
|
after = [ "bitcoind.service" ];
|
||||||
script = ''
|
script = ''
|
||||||
cd ${cfg.cliBase}/bin
|
cd ${cfg.cli}/bin
|
||||||
echo "Importing node banlist..."
|
echo "Importing node banlist..."
|
||||||
cat ${./banlist.cli.txt} | while read line; do
|
cat ${./banlist.cli.txt} | while read line; do
|
||||||
if ! err=$(eval "$line" 2>&1) && [[ $err != *already\ banned* ]]; then
|
if ! err=$(eval "$line" 2>&1) && [[ $err != *already\ banned* ]]; then
|
||||||
|
@ -112,7 +112,7 @@ in {
|
|||||||
configFile = builtins.toFile "config" ''
|
configFile = builtins.toFile "config" ''
|
||||||
network=${config.services.bitcoind.network}
|
network=${config.services.bitcoind.network}
|
||||||
btcrpcuser=${cfg.bitcoind.rpc.users.btcpayserver.name}
|
btcrpcuser=${cfg.bitcoind.rpc.users.btcpayserver.name}
|
||||||
btcrpcurl=http://${builtins.elemAt config.services.bitcoind.rpcbind 0}:${toString cfg.bitcoind.rpc.port}
|
btcrpcurl=http://${config.services.bitcoind.rpcbind}:${toString cfg.bitcoind.rpc.port}
|
||||||
btcnodeendpoint=${config.services.bitcoind.bind}:8333
|
btcnodeendpoint=${config.services.bitcoind.bind}:8333
|
||||||
bind=${cfg.nbxplorer.bind}
|
bind=${cfg.nbxplorer.bind}
|
||||||
port=${toString cfg.nbxplorer.port}
|
port=${toString cfg.nbxplorer.port}
|
||||||
|
@ -13,7 +13,7 @@ let
|
|||||||
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
|
${optionalString (cfg.proxy != null) "proxy=${cfg.proxy}"}
|
||||||
always-use-proxy=${if cfg.always-use-proxy then "true" else "false"}
|
always-use-proxy=${if cfg.always-use-proxy then "true" else "false"}
|
||||||
bind-addr=${cfg.bind-addr}:${toString cfg.bindport}
|
bind-addr=${cfg.bind-addr}:${toString cfg.bindport}
|
||||||
bitcoin-rpcconnect=${builtins.elemAt config.services.bitcoind.rpcbind 0}
|
bitcoin-rpcconnect=${config.services.bitcoind.rpcbind}
|
||||||
bitcoin-rpcport=${toString config.services.bitcoind.rpc.port}
|
bitcoin-rpcport=${toString config.services.bitcoind.rpc.port}
|
||||||
bitcoin-rpcuser=${config.services.bitcoind.rpc.users.public.name}
|
bitcoin-rpcuser=${config.services.bitcoind.rpc.users.public.name}
|
||||||
rpc-file-mode=0660
|
rpc-file-mode=0660
|
||||||
@ -38,12 +38,12 @@ in {
|
|||||||
};
|
};
|
||||||
proxy = mkOption {
|
proxy = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
|
||||||
description = "Set a socks proxy to use to connect to Tor nodes (or for all connections if *always-use-proxy* is set)";
|
description = "Set a socks proxy to use to connect to Tor nodes (or for all connections if *always-use-proxy* is set)";
|
||||||
};
|
};
|
||||||
always-use-proxy = mkOption {
|
always-use-proxy = mkOption {
|
||||||
type = types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = cfg.enforceTor;
|
||||||
description = ''
|
description = ''
|
||||||
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.
|
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.
|
||||||
'';
|
'';
|
||||||
|
@ -97,7 +97,7 @@ in {
|
|||||||
--daemon-dir='${bitcoind.dataDir}' \
|
--daemon-dir='${bitcoind.dataDir}' \
|
||||||
--electrum-rpc-addr=${cfg.address}:${toString cfg.port} \
|
--electrum-rpc-addr=${cfg.address}:${toString cfg.port} \
|
||||||
--monitoring-addr=${cfg.address}:${toString cfg.monitoringPort} \
|
--monitoring-addr=${cfg.address}:${toString cfg.monitoringPort} \
|
||||||
--daemon-rpc-addr=${builtins.elemAt bitcoind.rpcbind 0}:${toString bitcoind.rpc.port} \
|
--daemon-rpc-addr=${bitcoind.rpcbind}:${toString bitcoind.rpc.port} \
|
||||||
${cfg.extraArgs}
|
${cfg.extraArgs}
|
||||||
'';
|
'';
|
||||||
User = cfg.user;
|
User = cfg.user;
|
||||||
|
@ -20,7 +20,7 @@ let
|
|||||||
[BLOCKCHAIN]
|
[BLOCKCHAIN]
|
||||||
blockchain_source = bitcoin-rpc
|
blockchain_source = bitcoin-rpc
|
||||||
network = ${bitcoind.network}
|
network = ${bitcoind.network}
|
||||||
rpc_host = ${builtins.elemAt bitcoind.rpcbind 0}
|
rpc_host = ${bitcoind.rpcbind}
|
||||||
rpc_port = ${toString bitcoind.rpc.port}
|
rpc_port = ${toString bitcoind.rpc.port}
|
||||||
rpc_user = ${bitcoind.rpc.users.privileged.name}
|
rpc_user = ${bitcoind.rpc.users.privileged.name}
|
||||||
@@RPC_PASSWORD@@
|
@@RPC_PASSWORD@@
|
||||||
|
@ -7,14 +7,17 @@ let
|
|||||||
inherit (config) nix-bitcoin-services;
|
inherit (config) nix-bitcoin-services;
|
||||||
secretsDir = config.nix-bitcoin.secretsDir;
|
secretsDir = config.nix-bitcoin.secretsDir;
|
||||||
network = config.services.bitcoind.network;
|
network = config.services.bitcoind.network;
|
||||||
|
rpclisten = "${cfg.rpcAddress}:${toString cfg.rpcPort}";
|
||||||
configFile = builtins.toFile "loop.conf" ''
|
configFile = builtins.toFile "loop.conf" ''
|
||||||
datadir=${cfg.dataDir}
|
datadir=${cfg.dataDir}
|
||||||
network=${network}
|
network=${network}
|
||||||
|
rpclisten=${rpclisten}
|
||||||
|
restlisten=${cfg.restAddress}:${toString cfg.restPort}
|
||||||
logdir=${cfg.dataDir}/logs
|
logdir=${cfg.dataDir}/logs
|
||||||
tlscertpath=${secretsDir}/loop-cert
|
tlscertpath=${secretsDir}/loop-cert
|
||||||
tlskeypath=${secretsDir}/loop-key
|
tlskeypath=${secretsDir}/loop-key
|
||||||
|
|
||||||
lnd.host=${builtins.elemAt config.services.lnd.rpclisten 0}:${toString config.services.lnd.rpcPort}
|
lnd.host=${config.services.lnd.rpclisten}:${toString config.services.lnd.rpcPort}
|
||||||
lnd.macaroondir=${config.services.lnd.networkDir}
|
lnd.macaroondir=${config.services.lnd.networkDir}
|
||||||
lnd.tlspath=${secretsDir}/lnd-cert
|
lnd.tlspath=${secretsDir}/lnd-cert
|
||||||
|
|
||||||
@ -25,6 +28,26 @@ let
|
|||||||
in {
|
in {
|
||||||
options.services.lightning-loop = {
|
options.services.lightning-loop = {
|
||||||
enable = mkEnableOption "lightning-loop";
|
enable = mkEnableOption "lightning-loop";
|
||||||
|
rpcAddress = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "localhost";
|
||||||
|
description = "Address to listen for gRPC connections.";
|
||||||
|
};
|
||||||
|
rpcPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 11010;
|
||||||
|
description = "Port to listen for gRPC connections.";
|
||||||
|
};
|
||||||
|
restAddress = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = cfg.rpcAddress;
|
||||||
|
description = "Address to listen for REST connections.";
|
||||||
|
};
|
||||||
|
restPort = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 8081;
|
||||||
|
description = "Port to listen for REST connections.";
|
||||||
|
};
|
||||||
package = mkOption {
|
package = mkOption {
|
||||||
type = types.package;
|
type = types.package;
|
||||||
default = pkgs.nix-bitcoin.lightning-loop;
|
default = pkgs.nix-bitcoin.lightning-loop;
|
||||||
@ -38,7 +61,7 @@ in {
|
|||||||
};
|
};
|
||||||
proxy = mkOption {
|
proxy = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
|
||||||
description = "host:port of SOCKS5 proxy for connnecting to the loop server.";
|
description = "host:port of SOCKS5 proxy for connnecting to the loop server.";
|
||||||
};
|
};
|
||||||
extraConfig = mkOption {
|
extraConfig = mkOption {
|
||||||
@ -51,13 +74,13 @@ in {
|
|||||||
};
|
};
|
||||||
cli = mkOption {
|
cli = mkOption {
|
||||||
default = pkgs.writeScriptBin "loop" ''
|
default = pkgs.writeScriptBin "loop" ''
|
||||||
${cfg.cliExec} ${cfg.package}/bin/loop \
|
${cfg.package}/bin/loop \
|
||||||
|
--rpcserver ${rpclisten} \
|
||||||
--macaroonpath '${cfg.dataDir}/${network}/loop.macaroon' \
|
--macaroonpath '${cfg.dataDir}/${network}/loop.macaroon' \
|
||||||
--tlscertpath '${secretsDir}/loop-cert' "$@"
|
--tlscertpath '${secretsDir}/loop-cert' "$@"
|
||||||
'';
|
'';
|
||||||
description = "Binary to connect with the lightning-loop instance.";
|
description = "Binary to connect with the lightning-loop instance.";
|
||||||
};
|
};
|
||||||
inherit (nix-bitcoin-services) cliExec;
|
|
||||||
enforceTor = nix-bitcoin-services.enforceTor;
|
enforceTor = nix-bitcoin-services.enforceTor;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,11 +26,12 @@ let
|
|||||||
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
|
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
|
||||||
(attrValues cfg.rpc.users)
|
(attrValues cfg.rpc.users)
|
||||||
}
|
}
|
||||||
${lib.concatMapStrings (rpcbind: "rpcbind=${rpcbind}\n") cfg.rpcbind}
|
rpcbind=${cfg.rpcbind}
|
||||||
|
rpcconnect=${cfg.rpcbind}
|
||||||
${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
|
${lib.concatMapStrings (rpcallowip: "rpcallowip=${rpcallowip}\n") cfg.rpcallowip}
|
||||||
${optionalString (cfg.rpcuser != null) "rpcuser=${cfg.rpcuser}"}
|
${optionalString (cfg.rpcuser != null) "rpcuser=${cfg.rpcuser}"}
|
||||||
${optionalString (cfg.rpcpassword != null) "rpcpassword=${cfg.rpcpassword}"}
|
${optionalString (cfg.rpcpassword != null) "rpcpassword=${cfg.rpcpassword}"}
|
||||||
mainchainrpchost=${builtins.elemAt config.services.bitcoind.rpcbind 0}
|
mainchainrpchost=${config.services.bitcoind.rpcbind}
|
||||||
mainchainrpcport=${toString config.services.bitcoind.rpc.port}
|
mainchainrpcport=${toString config.services.bitcoind.rpc.port}
|
||||||
mainchainrpcuser=${config.services.bitcoind.rpc.users.public.name}
|
mainchainrpcuser=${config.services.bitcoind.rpc.users.public.name}
|
||||||
|
|
||||||
@ -125,8 +126,8 @@ in {
|
|||||||
};
|
};
|
||||||
|
|
||||||
rpcbind = mkOption {
|
rpcbind = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.str;
|
||||||
default = [ "127.0.0.1" ];
|
default = "127.0.0.1";
|
||||||
description = ''
|
description = ''
|
||||||
Bind to given address to listen for JSON-RPC connections.
|
Bind to given address to listen for JSON-RPC connections.
|
||||||
'';
|
'';
|
||||||
@ -160,7 +161,7 @@ in {
|
|||||||
};
|
};
|
||||||
proxy = mkOption {
|
proxy = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
|
||||||
description = "Connect through SOCKS5 proxy";
|
description = "Connect through SOCKS5 proxy";
|
||||||
};
|
};
|
||||||
listen = mkOption {
|
listen = mkOption {
|
||||||
@ -205,17 +206,16 @@ in {
|
|||||||
cli = mkOption {
|
cli = mkOption {
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
default = pkgs.writeScriptBin "elements-cli" ''
|
default = pkgs.writeScriptBin "elements-cli" ''
|
||||||
${cfg.cliExec} ${pkgs.nix-bitcoin.elementsd}/bin/elements-cli -datadir='${cfg.dataDir}' "$@"
|
${pkgs.nix-bitcoin.elementsd}/bin/elements-cli -datadir='${cfg.dataDir}' "$@"
|
||||||
'';
|
'';
|
||||||
description = "Binary to connect with the liquidd instance.";
|
description = "Binary to connect with the liquidd instance.";
|
||||||
};
|
};
|
||||||
swapCli = mkOption {
|
swapCli = mkOption {
|
||||||
default = pkgs.writeScriptBin "liquidswap-cli" ''
|
default = pkgs.writeScriptBin "liquidswap-cli" ''
|
||||||
${cfg.cliExec} ${pkgs.nix-bitcoin.liquid-swap}/bin/liquidswap-cli -c '${cfg.dataDir}/elements.conf' "$@"
|
${pkgs.nix-bitcoin.liquid-swap}/bin/liquidswap-cli -c '${cfg.dataDir}/elements.conf' "$@"
|
||||||
'';
|
'';
|
||||||
description = "Binary for managing liquid swaps.";
|
description = "Binary for managing liquid swaps.";
|
||||||
};
|
};
|
||||||
inherit (nix-bitcoin-services) cliExec;
|
|
||||||
enforceTor = nix-bitcoin-services.enforceTor;
|
enforceTor = nix-bitcoin-services.enforceTor;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ let
|
|||||||
secretsDir = config.nix-bitcoin.secretsDir;
|
secretsDir = config.nix-bitcoin.secretsDir;
|
||||||
|
|
||||||
bitcoind = config.services.bitcoind;
|
bitcoind = config.services.bitcoind;
|
||||||
bitcoindRpcAddress = builtins.elemAt bitcoind.rpcbind 0;
|
bitcoindRpcAddress = bitcoind.rpcbind;
|
||||||
onion-chef-service = (if cfg.announce-tor then [ "onion-chef.service" ] else []);
|
onion-chef-service = (if cfg.announce-tor then [ "onion-chef.service" ] else []);
|
||||||
networkDir = "${cfg.dataDir}/chain/bitcoin/${bitcoind.network}";
|
networkDir = "${cfg.dataDir}/chain/bitcoin/${bitcoind.network}";
|
||||||
configFile = pkgs.writeText "lnd.conf" ''
|
configFile = pkgs.writeText "lnd.conf" ''
|
||||||
@ -18,14 +18,14 @@ let
|
|||||||
tlskeypath=${secretsDir}/lnd-key
|
tlskeypath=${secretsDir}/lnd-key
|
||||||
|
|
||||||
listen=${toString cfg.listen}:${toString cfg.listenPort}
|
listen=${toString cfg.listen}:${toString cfg.listenPort}
|
||||||
${lib.concatMapStrings (rpclisten: "rpclisten=${rpclisten}:${toString cfg.rpcPort}\n") cfg.rpclisten}
|
rpclisten=${cfg.rpclisten}
|
||||||
${lib.concatMapStrings (restlisten: "restlisten=${restlisten}:${toString cfg.restPort}\n") cfg.restlisten}
|
restlisten=${cfg.restlisten}
|
||||||
|
|
||||||
bitcoin.${bitcoind.network}=1
|
bitcoin.${bitcoind.network}=1
|
||||||
bitcoin.active=1
|
bitcoin.active=1
|
||||||
bitcoin.node=bitcoind
|
bitcoin.node=bitcoind
|
||||||
|
|
||||||
tor.active=true
|
${optionalString (cfg.enforceTor) "tor.active=true"}
|
||||||
${optionalString (cfg.tor-socks != null) "tor.socks=${cfg.tor-socks}"}
|
${optionalString (cfg.tor-socks != null) "tor.socks=${cfg.tor-socks}"}
|
||||||
|
|
||||||
bitcoind.rpchost=${bitcoindRpcAddress}:${toString bitcoind.rpc.port}
|
bitcoind.rpchost=${bitcoindRpcAddress}:${toString bitcoind.rpc.port}
|
||||||
@ -66,15 +66,15 @@ in {
|
|||||||
description = "Bind to given port to listen to peer connections";
|
description = "Bind to given port to listen to peer connections";
|
||||||
};
|
};
|
||||||
rpclisten = mkOption {
|
rpclisten = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.str;
|
||||||
default = [ "localhost" ];
|
default = "localhost";
|
||||||
description = ''
|
description = ''
|
||||||
Bind to given address to listen to RPC connections.
|
Bind to given address to listen to RPC connections.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
restlisten = mkOption {
|
restlisten = mkOption {
|
||||||
type = types.listOf types.str;
|
type = types.str;
|
||||||
default = [ "localhost" ];
|
default = "localhost";
|
||||||
description = ''
|
description = ''
|
||||||
Bind to given address to listen to REST connections.
|
Bind to given address to listen to REST connections.
|
||||||
'';
|
'';
|
||||||
@ -91,7 +91,7 @@ in {
|
|||||||
};
|
};
|
||||||
tor-socks = mkOption {
|
tor-socks = mkOption {
|
||||||
type = types.nullOr types.str;
|
type = types.nullOr types.str;
|
||||||
default = null;
|
default = if cfg.enforceTor then config.services.tor.client.socksListenAddress else null;
|
||||||
description = "Set a socks proxy to use to connect to Tor nodes";
|
description = "Set a socks proxy to use to connect to Tor nodes";
|
||||||
};
|
};
|
||||||
announce-tor = mkOption {
|
announce-tor = mkOption {
|
||||||
@ -138,12 +138,13 @@ in {
|
|||||||
default = pkgs.writeScriptBin "lncli"
|
default = pkgs.writeScriptBin "lncli"
|
||||||
# Switch user because lnd makes datadir contents readable by user only
|
# Switch user because lnd makes datadir contents readable by user only
|
||||||
''
|
''
|
||||||
${cfg.cliExec} sudo -u lnd ${cfg.package}/bin/lncli --tlscertpath ${secretsDir}/lnd-cert \
|
sudo -u lnd ${cfg.package}/bin/lncli \
|
||||||
|
--rpcserver ${cfg.rpclisten}:${toString cfg.rpcPort} \
|
||||||
|
--tlscertpath '${secretsDir}/lnd-cert' \
|
||||||
--macaroonpath '${networkDir}/admin.macaroon' "$@"
|
--macaroonpath '${networkDir}/admin.macaroon' "$@"
|
||||||
'';
|
'';
|
||||||
description = "Binary to connect with the lnd instance.";
|
description = "Binary to connect with the lnd instance.";
|
||||||
};
|
};
|
||||||
inherit (nix-bitcoin-services) cliExec;
|
|
||||||
enforceTor = nix-bitcoin-services.enforceTor;
|
enforceTor = nix-bitcoin-services.enforceTor;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -188,12 +189,12 @@ in {
|
|||||||
RestartSec = "10s";
|
RestartSec = "10s";
|
||||||
ReadWritePaths = "${cfg.dataDir}";
|
ReadWritePaths = "${cfg.dataDir}";
|
||||||
ExecStartPost = let
|
ExecStartPost = let
|
||||||
restPort = toString cfg.restPort;
|
restUrl = "https://${cfg.restlisten}:${toString cfg.restPort}/v1";
|
||||||
in [
|
in [
|
||||||
# Run fully privileged for secrets dir write access
|
# Run fully privileged for secrets dir write access
|
||||||
"+${nix-bitcoin-services.script ''
|
"+${nix-bitcoin-services.script ''
|
||||||
attempts=250
|
attempts=250
|
||||||
while ! { exec 3>/dev/tcp/127.0.0.1/${restPort} && exec 3>&-; } &>/dev/null; do
|
while ! { exec 3>/dev/tcp/${cfg.restlisten}/${toString cfg.restPort} && exec 3>&-; } &>/dev/null; do
|
||||||
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
|
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
@ -204,7 +205,7 @@ in {
|
|||||||
umask u=r,go=
|
umask u=r,go=
|
||||||
${pkgs.curl}/bin/curl -s \
|
${pkgs.curl}/bin/curl -s \
|
||||||
--cacert ${secretsDir}/lnd-cert \
|
--cacert ${secretsDir}/lnd-cert \
|
||||||
-X GET https://127.0.0.1:${restPort}/v1/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic"
|
-X GET ${restUrl}/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic"
|
||||||
fi
|
fi
|
||||||
chown lnd: "$mnemonic"
|
chown lnd: "$mnemonic"
|
||||||
''}"
|
''}"
|
||||||
@ -216,7 +217,7 @@ in {
|
|||||||
--cacert ${secretsDir}/lnd-cert \
|
--cacert ${secretsDir}/lnd-cert \
|
||||||
-X POST -d "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\", \
|
-X POST -d "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\", \
|
||||||
\"cipher_seed_mnemonic\": $(cat ${secretsDir}/lnd-seed-mnemonic | tr -d '\n')}" \
|
\"cipher_seed_mnemonic\": $(cat ${secretsDir}/lnd-seed-mnemonic | tr -d '\n')}" \
|
||||||
https://127.0.0.1:${restPort}/v1/initwallet
|
${restUrl}/initwallet
|
||||||
|
|
||||||
# Guarantees that RPC calls with cfg.cli succeed after the service is started
|
# Guarantees that RPC calls with cfg.cli succeed after the service is started
|
||||||
echo Wait until wallet is created
|
echo Wait until wallet is created
|
||||||
@ -231,11 +232,11 @@ in {
|
|||||||
--cacert ${secretsDir}/lnd-cert \
|
--cacert ${secretsDir}/lnd-cert \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\"}" \
|
-d "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\"}" \
|
||||||
https://127.0.0.1:${restPort}/v1/unlockwallet
|
${restUrl}/unlockwallet
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Wait until the RPC port is open
|
# Wait until the RPC port is open
|
||||||
while ! { exec 3>/dev/tcp/127.0.0.1/${toString cfg.rpcPort}; } &>/dev/null; do
|
while ! { exec 3>/dev/tcp/${cfg.rpclisten}/${toString cfg.rpcPort}; } &>/dev/null; do
|
||||||
sleep 0.1
|
sleep 0.1
|
||||||
done
|
done
|
||||||
|
|
||||||
@ -251,7 +252,7 @@ in {
|
|||||||
--cacert ${secretsDir}/lnd-cert \
|
--cacert ${secretsDir}/lnd-cert \
|
||||||
-X POST \
|
-X POST \
|
||||||
-d '{"permissions":[${cfg.macaroons.${macaroon}.permissions}]}' \
|
-d '{"permissions":[${cfg.macaroons.${macaroon}.permissions}]}' \
|
||||||
https://127.0.0.1:${restPort}/v1/macaroon |\
|
${restUrl}/macaroon |\
|
||||||
${pkgs.jq}/bin/jq -c '.macaroon' | ${pkgs.xxd}/bin/xxd -p -r > "$macaroonPath"
|
${pkgs.jq}/bin/jq -c '.macaroon' | ${pkgs.xxd}/bin/xxd -p -r > "$macaroonPath"
|
||||||
chown ${cfg.macaroons.${macaroon}.user}: "$macaroonPath"
|
chown ${cfg.macaroons.${macaroon}.user}: "$macaroonPath"
|
||||||
'') (attrNames cfg.macaroons)}
|
'') (attrNames cfg.macaroons)}
|
||||||
|
@ -50,7 +50,7 @@ in {
|
|||||||
|
|
||||||
addressblock = mkOption {
|
addressblock = mkOption {
|
||||||
type = types.ints.u8;
|
type = types.ints.u8;
|
||||||
default = "1";
|
default = 1;
|
||||||
description = ''
|
description = ''
|
||||||
The address block N in 169.254.N.0/24, used as the prefix for netns addresses.
|
The address block N in 169.254.N.0/24, used as the prefix for netns addresses.
|
||||||
'';
|
'';
|
||||||
@ -141,6 +141,7 @@ in {
|
|||||||
inherit (v) netnsName;
|
inherit (v) netnsName;
|
||||||
ipNetns = "${ip} -n ${netnsName}";
|
ipNetns = "${ip} -n ${netnsName}";
|
||||||
netnsIptables = "${ip} netns exec ${netnsName} ${config.networking.firewall.package}/bin/iptables";
|
netnsIptables = "${ip} netns exec ${netnsName} ${config.networking.firewall.package}/bin/iptables";
|
||||||
|
allowedAddresses = concatMapStringsSep "," (available: netns.${available}.address) v.availableNetns;
|
||||||
in {
|
in {
|
||||||
"${n}".serviceConfig.NetworkNamespacePath = "/var/run/netns/${netnsName}";
|
"${n}".serviceConfig.NetworkNamespacePath = "/var/run/netns/${netnsName}";
|
||||||
|
|
||||||
@ -151,6 +152,7 @@ in {
|
|||||||
requiredBy = bindsTo;
|
requiredBy = bindsTo;
|
||||||
before = bindsTo;
|
before = bindsTo;
|
||||||
script = ''
|
script = ''
|
||||||
|
${ip} netns delete ${netnsName} 2> /dev/null || true
|
||||||
${ip} netns add ${netnsName}
|
${ip} netns add ${netnsName}
|
||||||
${ipNetns} link set lo up
|
${ipNetns} link set lo up
|
||||||
${ip} link add ${veth} type veth peer name ${peer}
|
${ip} link add ${veth} type veth peer name ${peer}
|
||||||
@ -164,23 +166,19 @@ in {
|
|||||||
${netnsIptables} -w -A INPUT -s 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
|
${netnsIptables} -w -A INPUT -s 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
|
||||||
# allow return traffic to outgoing connections initiated by the service itself
|
# allow return traffic to outgoing connections initiated by the service itself
|
||||||
${netnsIptables} -w -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
|
${netnsIptables} -w -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
|
||||||
'' + (optionalString (config.services.${n}.enforceTor or false)) ''
|
'' + optionalString (config.services.${n}.enforceTor or false) ''
|
||||||
${netnsIptables} -w -P OUTPUT DROP
|
${netnsIptables} -w -P OUTPUT DROP
|
||||||
${netnsIptables} -w -A OUTPUT -d 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
|
${netnsIptables} -w -A OUTPUT -d 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
|
||||||
'' + concatMapStrings (otherNetns: let
|
'' + optionalString (v.availableNetns != []) ''
|
||||||
other = netns.${otherNetns};
|
${netnsIptables} -w -A INPUT -s ${allowedAddresses} -j ACCEPT
|
||||||
in ''
|
${netnsIptables} -w -A OUTPUT -d ${allowedAddresses} -j ACCEPT
|
||||||
${netnsIptables} -w -A INPUT -s ${other.address} -j ACCEPT
|
'';
|
||||||
${netnsIptables} -w -A OUTPUT -d ${other.address} -j ACCEPT
|
|
||||||
'') v.availableNetns;
|
|
||||||
preStop = ''
|
preStop = ''
|
||||||
${ip} netns delete ${netnsName}
|
${ip} netns delete ${netnsName}
|
||||||
${ip} link del ${peer}
|
|
||||||
'';
|
'';
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
Type = "oneshot";
|
Type = "oneshot";
|
||||||
RemainAfterExit = "yes";
|
RemainAfterExit = "yes";
|
||||||
ExecStartPre = "-${ip} netns delete ${netnsName}";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -252,18 +250,11 @@ in {
|
|||||||
|
|
||||||
services.bitcoind = {
|
services.bitcoind = {
|
||||||
bind = netns.bitcoind.address;
|
bind = netns.bitcoind.address;
|
||||||
rpcbind = [
|
rpcbind = netns.bitcoind.address;
|
||||||
"${netns.bitcoind.address}"
|
|
||||||
"127.0.0.1"
|
|
||||||
];
|
|
||||||
rpcallowip = [
|
rpcallowip = [
|
||||||
"127.0.0.1"
|
bridgeIp # For operator user
|
||||||
] ++ map (n: "${netns.${n}.address}") netns.bitcoind.availableNetns;
|
netns.bitcoind.address
|
||||||
cli = let
|
] ++ map (n: netns.${n}.address) netns.bitcoind.availableNetns;
|
||||||
inherit (config.services.bitcoind) cliBase;
|
|
||||||
in pkgs.writeScriptBin cliBase.name ''
|
|
||||||
exec netns-exec ${netns.bitcoind.netnsName} ${cliBase}/bin/${cliBase.name} "$@"
|
|
||||||
'';
|
|
||||||
};
|
};
|
||||||
systemd.services.bitcoind-import-banlist.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-bitcoind";
|
systemd.services.bitcoind-import-banlist.serviceConfig.NetworkNamespacePath = "/var/run/netns/nb-bitcoind";
|
||||||
|
|
||||||
@ -271,27 +262,17 @@ in {
|
|||||||
|
|
||||||
services.lnd = {
|
services.lnd = {
|
||||||
listen = netns.lnd.address;
|
listen = netns.lnd.address;
|
||||||
rpclisten = [
|
rpclisten = netns.lnd.address;
|
||||||
"${netns.lnd.address}"
|
restlisten = netns.lnd.address;
|
||||||
"127.0.0.1"
|
|
||||||
];
|
|
||||||
restlisten = [
|
|
||||||
"${netns.lnd.address}"
|
|
||||||
"127.0.0.1"
|
|
||||||
];
|
|
||||||
cliExec = mkCliExec "lnd";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
services.liquidd = {
|
services.liquidd = {
|
||||||
bind = netns.liquidd.address;
|
bind = netns.liquidd.address;
|
||||||
rpcbind = [
|
rpcbind = netns.liquidd.address;
|
||||||
"${netns.liquidd.address}"
|
|
||||||
"127.0.0.1"
|
|
||||||
];
|
|
||||||
rpcallowip = [
|
rpcallowip = [
|
||||||
"127.0.0.1"
|
bridgeIp # For operator user
|
||||||
] ++ map (n: "${netns.${n}.address}") netns.liquidd.availableNetns;
|
netns.liquidd.address
|
||||||
cliExec = mkCliExec "liquidd";
|
] ++ map (n: netns.${n}.address) netns.liquidd.availableNetns;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.electrs.address = netns.electrs.address;
|
services.electrs.address = netns.electrs.address;
|
||||||
@ -308,7 +289,7 @@ in {
|
|||||||
host = netns.nanopos.address;
|
host = netns.nanopos.address;
|
||||||
};
|
};
|
||||||
|
|
||||||
services.lightning-loop.cliExec = mkCliExec "lightning-loop";
|
services.lightning-loop.rpcAddress = netns.lightning-loop.address;
|
||||||
|
|
||||||
services.nbxplorer.bind = netns.nbxplorer.address;
|
services.nbxplorer.bind = netns.nbxplorer.address;
|
||||||
services.btcpayserver.bind = netns.btcpayserver.address;
|
services.btcpayserver.bind = netns.btcpayserver.address;
|
||||||
|
@ -49,17 +49,11 @@ in {
|
|||||||
hiddenServices.sshd = mkHiddenService { port = 22; };
|
hiddenServices.sshd = mkHiddenService { port = 22; };
|
||||||
};
|
};
|
||||||
|
|
||||||
# netns-isolation
|
|
||||||
nix-bitcoin.netns-isolation = {
|
|
||||||
addressblock = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
# bitcoind
|
# bitcoind
|
||||||
services.bitcoind = {
|
services.bitcoind = {
|
||||||
enable = true;
|
enable = true;
|
||||||
listen = true;
|
listen = true;
|
||||||
dataDirReadableByGroup = mkIf cfg.electrs.high-memory true;
|
dataDirReadableByGroup = mkIf cfg.electrs.high-memory true;
|
||||||
proxy = cfg.tor.client.socksListenAddress;
|
|
||||||
enforceTor = true;
|
enforceTor = true;
|
||||||
port = 8333;
|
port = 8333;
|
||||||
assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6";
|
assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6";
|
||||||
@ -74,11 +68,7 @@ in {
|
|||||||
services.tor.hiddenServices.bitcoind = mkHiddenService { port = cfg.bitcoind.port; toHost = cfg.bitcoind.bind; };
|
services.tor.hiddenServices.bitcoind = mkHiddenService { port = cfg.bitcoind.port; toHost = cfg.bitcoind.bind; };
|
||||||
|
|
||||||
# clightning
|
# clightning
|
||||||
services.clightning = {
|
services.clightning.enforceTor = true;
|
||||||
proxy = cfg.tor.client.socksListenAddress;
|
|
||||||
enforceTor = true;
|
|
||||||
always-use-proxy = true;
|
|
||||||
};
|
|
||||||
services.tor.hiddenServices.clightning = mkIf cfg.clightning.enable (mkHiddenService {
|
services.tor.hiddenServices.clightning = mkIf cfg.clightning.enable (mkHiddenService {
|
||||||
port = cfg.clightning.onionport;
|
port = cfg.clightning.onionport;
|
||||||
toHost = cfg.clightning.bind-addr;
|
toHost = cfg.clightning.bind-addr;
|
||||||
@ -86,17 +76,11 @@ in {
|
|||||||
});
|
});
|
||||||
|
|
||||||
# lnd
|
# lnd
|
||||||
services.lnd = {
|
services.lnd.enforceTor = true;
|
||||||
tor-socks = cfg.tor.client.socksListenAddress;
|
|
||||||
enforceTor = true;
|
|
||||||
};
|
|
||||||
services.tor.hiddenServices.lnd = mkIf cfg.lnd.enable (mkHiddenService { port = cfg.lnd.onionport; toHost = cfg.lnd.listen; toPort = cfg.lnd.listenPort; });
|
services.tor.hiddenServices.lnd = mkIf cfg.lnd.enable (mkHiddenService { port = cfg.lnd.onionport; toHost = cfg.lnd.listen; toPort = cfg.lnd.listenPort; });
|
||||||
|
|
||||||
# lightning-loop
|
# lightning-loop
|
||||||
services.lightning-loop = {
|
services.lightning-loop.enforceTor = true;
|
||||||
proxy = cfg.tor.client.socksListenAddress;
|
|
||||||
enforceTor = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
# liquidd
|
# liquidd
|
||||||
services.liquidd = {
|
services.liquidd = {
|
||||||
@ -104,7 +88,6 @@ in {
|
|||||||
prune = 1000;
|
prune = 1000;
|
||||||
validatepegin = true;
|
validatepegin = true;
|
||||||
listen = true;
|
listen = true;
|
||||||
proxy = cfg.tor.client.socksListenAddress;
|
|
||||||
enforceTor = true;
|
enforceTor = true;
|
||||||
port = 7042;
|
port = 7042;
|
||||||
};
|
};
|
||||||
|
@ -10,10 +10,6 @@
|
|||||||
#include <sys/capability.h>
|
#include <sys/capability.h>
|
||||||
|
|
||||||
static char *allowed_netns[] = {
|
static char *allowed_netns[] = {
|
||||||
"nb-lnd",
|
|
||||||
"nb-lightning-loop",
|
|
||||||
"nb-bitcoind",
|
|
||||||
"nb-liquidd",
|
|
||||||
"nb-joinmarket"
|
"nb-joinmarket"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -169,12 +169,12 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
# A basic subset of tests to keep the total runtime within
|
# A basic subset of tests to keep the total runtime within
|
||||||
# manageable bounds (<3 min on desktop systems).
|
# manageable bounds (<4 min on desktop systems).
|
||||||
# These are also run on the CI server.
|
# These are also run on the CI server.
|
||||||
basic() {
|
basic() {
|
||||||
scenario=default buildTest "$@"
|
scenario=default buildTest "$@"
|
||||||
scenario=netns buildTest "$@"
|
scenario=netns buildTest "$@"
|
||||||
scenario=full evalTest "$@"
|
scenario=netnsRegtest buildTest "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
all() {
|
all() {
|
||||||
@ -182,6 +182,7 @@ all() {
|
|||||||
scenario=netns buildTest "$@"
|
scenario=netns buildTest "$@"
|
||||||
scenario=full buildTest "$@"
|
scenario=full buildTest "$@"
|
||||||
scenario=regtest buildTest "$@"
|
scenario=regtest buildTest "$@"
|
||||||
|
scenario=netnsRegtest buildTest "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
|
@ -111,11 +111,7 @@ let testEnv = rec {
|
|||||||
};
|
};
|
||||||
|
|
||||||
netns = {
|
netns = {
|
||||||
imports = [ scenarios.secureNode ];
|
imports = with scenarios; [ netnsBase secureNode ];
|
||||||
nix-bitcoin.netns-isolation.enable = true;
|
|
||||||
test.data.netns = config.nix-bitcoin.netns-isolation.netns;
|
|
||||||
tests.netns-isolation = true;
|
|
||||||
|
|
||||||
# This test is rather slow and unaffected by netns settings
|
# This test is rather slow and unaffected by netns settings
|
||||||
tests.backups = mkForce false;
|
tests.backups = mkForce false;
|
||||||
};
|
};
|
||||||
@ -132,6 +128,18 @@ let testEnv = rec {
|
|||||||
services.joinmarket.enable = true;
|
services.joinmarket.enable = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# netns and regtest, without secure-node.nix
|
||||||
|
netnsRegtest = {
|
||||||
|
imports = with scenarios; [ netnsBase regtest ];
|
||||||
|
};
|
||||||
|
|
||||||
|
netnsBase = {
|
||||||
|
nix-bitcoin.netns-isolation.enable = true;
|
||||||
|
test.data.netns = config.nix-bitcoin.netns-isolation.netns;
|
||||||
|
tests.netns-isolation = true;
|
||||||
|
environment.systemPackages = [ pkgs.fping ];
|
||||||
|
};
|
||||||
|
|
||||||
regtestBase = {
|
regtestBase = {
|
||||||
tests.regtest = true;
|
tests.regtest = true;
|
||||||
|
|
||||||
|
@ -237,49 +237,45 @@ def _():
|
|||||||
# (and their corresponding network namespaces).
|
# (and their corresponding network namespaces).
|
||||||
@test("netns-isolation")
|
@test("netns-isolation")
|
||||||
def _():
|
def _():
|
||||||
ping_bitcoind = "ip netns exec nb-bitcoind ping -c 1 -w 1"
|
def get_ips(services):
|
||||||
ping_nanopos = "ip netns exec nb-nanopos ping -c 1 -w 1"
|
enabled = enabled_tests.intersection(services)
|
||||||
ping_nbxplorer = "ip netns exec nb-nbxplorer ping -c 1 -w 1"
|
return " ".join(ip(service) for service in enabled)
|
||||||
|
|
||||||
# Positive ping tests (non-exhaustive)
|
def assert_reachable(src, dests):
|
||||||
machine.succeed(
|
dest_ips = get_ips(dests)
|
||||||
"%s %s &&" % (ping_bitcoind, ip("bitcoind"))
|
if src in enabled_tests and dest_ips:
|
||||||
+ "%s %s &&" % (ping_bitcoind, ip("clightning"))
|
machine.succeed(f"ip netns exec nb-{src} fping -c1 -t100 {dest_ips}")
|
||||||
+ "%s %s &&" % (ping_bitcoind, ip("lnd"))
|
|
||||||
+ "%s %s &&" % (ping_bitcoind, ip("liquidd"))
|
|
||||||
+ "%s %s &&" % (ping_bitcoind, ip("nbxplorer"))
|
|
||||||
+ "%s %s &&" % (ping_nbxplorer, ip("btcpayserver"))
|
|
||||||
+ "%s %s &&" % (ping_nanopos, ip("lightning-charge"))
|
|
||||||
+ "%s %s &&" % (ping_nanopos, ip("nanopos"))
|
|
||||||
+ "%s %s" % (ping_nanopos, ip("nginx"))
|
|
||||||
)
|
|
||||||
|
|
||||||
# Negative ping tests (non-exhaustive)
|
def assert_unreachable(src, dests):
|
||||||
|
dest_ips = get_ips(dests)
|
||||||
|
if src in enabled_tests and dest_ips:
|
||||||
machine.fail(
|
machine.fail(
|
||||||
"%s %s ||" % (ping_bitcoind, ip("spark-wallet"))
|
# This fails when no host is reachable within 100 ms
|
||||||
+ "%s %s ||" % (ping_bitcoind, ip("lightning-loop"))
|
f"ip netns exec nb-{src} fping -c1 -t100 --reachable=1 {dest_ips}"
|
||||||
+ "%s %s ||" % (ping_bitcoind, ip("lightning-charge"))
|
|
||||||
+ "%s %s ||" % (ping_bitcoind, ip("nanopos"))
|
|
||||||
+ "%s %s ||" % (ping_bitcoind, ip("nginx"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("bitcoind"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("clightning"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("lnd"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("lightning-loop"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("liquidd"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("electrs"))
|
|
||||||
+ "%s %s ||" % (ping_nanopos, ip("spark-wallet"))
|
|
||||||
+ "%s %s" % (ping_nanopos, ip("btcpayserver"))
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# test that netns-exec can't be run for unauthorized namespace
|
# These reachability tests are non-exhaustive
|
||||||
machine.fail("netns-exec nb-electrs ip a")
|
assert_reachable("bitcoind", ["clightning", "lnd", "liquidd"])
|
||||||
|
assert_unreachable("bitcoind", ["btcpayserver", "spark-wallet", "lightning-loop"])
|
||||||
|
assert_unreachable("btcpayserver", ["bitcoind", "lightning-loop", "liquidd"])
|
||||||
|
|
||||||
# test that netns-exec drops capabilities
|
# netns addresses can not be bound to in the main netns.
|
||||||
|
# This prevents processes in the main netns from impersonating nix-bitcoin services.
|
||||||
|
assert_matches(
|
||||||
|
f"nc -l {ip('bitcoind')} 1080 2>&1 || true", "nc: Cannot assign requested address"
|
||||||
|
)
|
||||||
|
|
||||||
|
if "joinmarket" in enabled_tests:
|
||||||
|
# netns-exec should drop capabilities
|
||||||
assert_full_match(
|
assert_full_match(
|
||||||
"su operator -c 'netns-exec nb-bitcoind capsh --print | grep Current '", "Current: =\n"
|
"su operator -c 'netns-exec nb-joinmarket capsh --print | grep Current'", "Current: =\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# test that netns-exec can not be executed by users that are not operator
|
if "clightning" in enabled_tests:
|
||||||
|
# netns-exec should fail for unauthorized namespaces
|
||||||
|
machine.fail("netns-exec nb-clightning ip a")
|
||||||
|
|
||||||
|
# netns-exec should only be executable by the operator user
|
||||||
machine.fail("sudo -u clightning netns-exec nb-bitcoind ip a")
|
machine.fail("sudo -u clightning netns-exec nb-bitcoind ip a")
|
||||||
|
|
||||||
|
|
||||||
@ -347,7 +343,7 @@ def _():
|
|||||||
machine.wait_until_succeeds(
|
machine.wait_until_succeeds(
|
||||||
log_has_string("lightning-loop", "Starting event loop at height 10")
|
log_has_string("lightning-loop", "Starting event loop at height 10")
|
||||||
)
|
)
|
||||||
succeed("sudo -u operator loop getparams | jq -e '.rules'")
|
succeed("sudo -u operator loop getparams")
|
||||||
|
|
||||||
|
|
||||||
if "netns-isolation" in enabled_tests:
|
if "netns-isolation" in enabled_tests:
|
||||||
|
Loading…
Reference in New Issue
Block a user