services: add tor.* options

Split `enforceTor` into `tor.proxy` and `tor.enforce`.
By enabling `tor.proxy` without `tor.enforce`, a service can accept
incoming clearnet connections.
E.g., this allows setting up a Tor-proxied bitcoind node that accepts
RPC connections from LAN.
This commit is contained in:
Erik Arvstedt 2021-11-28 21:24:49 +01:00
parent ff24e73ad7
commit 9bda7305fd
No known key found for this signature in database
GPG Key ID: 33312B944DD97846
17 changed files with 109 additions and 65 deletions

View File

@ -157,7 +157,7 @@ let
};
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null;
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
description = "Connect through SOCKS5 proxy";
};
i2p = mkOption {
@ -262,7 +262,7 @@ let
'';
description = "Binary to connect with the bitcoind instance.";
};
enforceTor = nbLib.enforceTor;
tor = nbLib.tor;
};
};
@ -407,7 +407,7 @@ in {
Restart = "on-failure";
UMask = mkIf cfg.dataDirReadableByGroup "0027";
ReadWritePaths = cfg.dataDir;
} // nbLib.allowedIPAddresses cfg.enforceTor
} // nbLib.allowedIPAddresses cfg.tor.enforce
// optionalAttrs zmqServerEnabled nbLib.allowNetlink;
};

View File

@ -54,7 +54,7 @@ let
default = cfg.btcpayserver.user;
description = "The group as which to run btcpayserver.";
};
enforceTor = nbLib.enforceTor;
tor.enforce = nbLib.tor.enforce;
};
nbxplorer = {
@ -96,7 +96,7 @@ let
default = cfg.nbxplorer.user;
description = "The group as which to run nbxplorer.";
};
enforceTor = nbLib.enforceTor;
tor.enforce = nbLib.tor.enforce;
};
};
@ -185,7 +185,7 @@ in {
RestartSec = "10s";
ReadWritePaths = cfg.nbxplorer.dataDir;
MemoryDenyWriteExecute = "false";
} // nbLib.allowedIPAddresses cfg.nbxplorer.enforceTor;
} // nbLib.allowedIPAddresses cfg.nbxplorer.tor.enforce;
};
systemd.services.btcpayserver = let
@ -238,7 +238,7 @@ in {
RestartSec = "10s";
ReadWritePaths = cfg.btcpayserver.dataDir;
MemoryDenyWriteExecute = "false";
} // nbLib.allowedIPAddresses cfg.btcpayserver.enforceTor;
} // nbLib.allowedIPAddresses cfg.btcpayserver.tor.enforce;
}; in self;
users.users.${cfg.nbxplorer.user} = {

View File

@ -28,6 +28,6 @@ let cfg = config.services.clightning.plugins.clboss; in
'';
systemd.services.clightning.path = [
pkgs.dnsutils
] ++ optional config.services.clightning.enforceTor (hiPrio config.nix-bitcoin.torify);
] ++ optional config.services.clightning.tor.proxy (hiPrio config.nix-bitcoin.torify);
};
}

View File

@ -16,14 +16,14 @@ let
};
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null;
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
description = ''
Socks proxy for connecting to Tor nodes (or for all connections if option always-use-proxy is set).
'';
};
always-use-proxy = mkOption {
type = types.bool;
default = cfg.enforceTor;
default = cfg.tor.proxy;
description = ''
Always use the proxy, even to connect to normal IP addresses.
You can still connect to Unix domain sockets manually.
@ -79,7 +79,7 @@ let
If left empty, no address is announced.
'';
};
inherit (nbLib) enforceTor;
tor = nbLib.tor;
};
cfg = config.services.clightning;
@ -156,7 +156,7 @@ in {
#
# Disable seccomp filtering because clightning depends on this syscall.
SystemCallFilter = [];
} // nbLib.allowedIPAddresses cfg.enforceTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
# Wait until the rpc socket appears
postStart = ''
while [[ ! -e ${cfg.networkDir}/lightning-rpc ]]; do

View File

@ -39,7 +39,7 @@ let
default = cfg.user;
description = "The group as which to run electrs.";
};
enforceTor = nbLib.enforceTor;
tor.enforce = nbLib.tor.enforce;
};
cfg = config.services.electrs;
@ -94,7 +94,7 @@ in {
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.dataDir;
} // nbLib.allowedIPAddresses cfg.enforceTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
};
users.users.${cfg.user} = {

View File

@ -29,11 +29,9 @@ let
default = cfg.user;
description = "The group as which to run JoinMarket.";
};
# This option is only used by netns-isolation
enforceTor = mkOption {
readOnly = true;
default = true;
};
# This option is only used by netns-isolation.
# Tor is always enabled.
tor.enforce = nbLib.tor.enforce;
};
cfg = config.services.joinmarket-ob-watcher;
@ -100,7 +98,7 @@ in {
SystemCallFilter = nbLib.defaultHardening.SystemCallFilter ++ [ "mbind" ] ;
Restart = "on-failure";
RestartSec = "10s";
} // nbLib.allowTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
};
users.users.${cfg.user} = {

View File

@ -50,11 +50,9 @@ let
readOnly = true;
default = ircServers;
};
# This option is only used by netns-isolation
enforceTor = mkOption {
readOnly = true;
default = true;
};
# This option is only used by netns-isolation.
# Tor is always enabled.
tor.enforce = nbLib.tor.enforce;
inherit (nbLib) cliExec;
yieldgenerator = {
@ -328,7 +326,7 @@ in {
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.dataDir;
} // nbLib.allowTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
};
users.users.${cfg.user} = {

View File

@ -36,7 +36,7 @@ let
};
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null;
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
description = "host:port of SOCKS5 proxy for connnecting to the loop server.";
};
extraConfig = mkOption {
@ -56,7 +56,7 @@ let
'';
description = "Binary to connect with the lightning-loop instance.";
};
enforceTor = nbLib.enforceTor;
tor = nbLib.tor;
};
cfg = config.services.lightning-loop;
@ -105,7 +105,7 @@ in {
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.dataDir;
} // nbLib.allowedIPAddresses cfg.enforceTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
};
nix-bitcoin.secrets = {

View File

@ -36,7 +36,7 @@ let
};
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null;
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
description = "host:port of SOCKS5 proxy for connnecting to the pool auction server.";
};
extraConfig = mkOption {
@ -56,7 +56,7 @@ let
'';
description = "Binary to connect with the lightning-pool instance.";
};
enforceTor = nbLib.enforceTor;
tor = nbLib.tor;
};
cfg = config.services.lightning-pool;
@ -102,7 +102,7 @@ in {
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.dataDir;
} // (nbLib.allowedIPAddresses cfg.enforceTor)
} // (nbLib.allowedIPAddresses cfg.tor.enforce)
// nbLib.allowNetlink; # required by gRPC-Go
};
};

View File

@ -98,7 +98,7 @@ let
};
proxy = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null;
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
description = "Connect through SOCKS5 proxy";
};
dbCache = mkOption {
@ -156,7 +156,7 @@ let
'';
description = "Binary for managing liquid swaps.";
};
enforceTor = nbLib.enforceTor;
tor = nbLib.tor;
};
};
@ -271,7 +271,7 @@ in {
ExecStart = "${nbPkgs.elementsd}/bin/elementsd -datadir='${cfg.dataDir}'";
Restart = "on-failure";
ReadWritePaths = cfg.dataDir;
} // nbLib.allowedIPAddresses cfg.enforceTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
};
users.users.${cfg.user} = {

View File

@ -46,7 +46,7 @@ let
};
tor-socks = mkOption {
type = types.nullOr types.str;
default = if cfg.enforceTor then config.nix-bitcoin.torClientAddressWithPort else null;
default = if cfg.tor.proxy then config.nix-bitcoin.torClientAddressWithPort else null;
description = "Socks proxy for connecting to Tor nodes";
};
macaroons = mkOption {
@ -117,7 +117,7 @@ let
default = "${secretsDir}/lnd-cert";
description = "LND TLS certificate path.";
};
inherit (nbLib) enforceTor;
tor = nbLib.tor;
};
cfg = config.services.lnd;
@ -143,7 +143,7 @@ let
bitcoin.active=1
bitcoin.node=bitcoind
${optionalString (cfg.enforceTor) "tor.active=true"}
${optionalString (cfg.tor.proxy) "tor.active=true"}
${optionalString (cfg.tor-socks != null) "tor.socks=${cfg.tor-socks}"}
bitcoind.rpchost=${bitcoindRpcAddress}:${toString bitcoind.rpc.port}
@ -277,7 +277,7 @@ in {
'') (attrNames cfg.macaroons)}
'')
];
} // nbLib.allowedIPAddresses cfg.enforceTor;
} // nbLib.allowedIPAddresses cfg.tor.enforce;
};
users.users.${cfg.user} = {

View File

@ -179,7 +179,7 @@ in {
${iptables} -w -A INPUT -s 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
# allow return traffic to outgoing connections initiated by the service itself
${iptables} -w -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
'' + optionalString (config.services.${n}.enforceTor or false) ''
'' + optionalString (config.services.${n}.tor.enforce or false) ''
${iptables} -w -P OUTPUT DROP
${iptables} -w -A OUTPUT -d 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
'' + optionalString (v.availableNetns != []) ''

View File

@ -7,6 +7,16 @@ let
mkRemovedOptionModule [ "services" service "announce-tor" ] ''
Use option `nix-bitcoin.onionServices.${service}.public` instead.
'';
mkSplitEnforceTorOption = service:
(mkRemovedOptionModule [ "services" service "enforceTor" ] ''
The option has been split into options `tor.proxy` and `tor.enforce`.
Set `tor.proxy = true` to proxy outgoing connections with Tor.
Set `tor.enforce = true` to only allow connections (incoming and outgoing) through Tor.
'');
mkRenamedEnforceTorOption = service:
(mkRenamedOptionModule [ "services" service "enforceTor" ] [ "services" service "tor" "enforce" ]);
in {
imports = [
(mkRenamedOptionModule [ "services" "bitcoind" "bind" ] [ "services" "bitcoind" "address" ])
@ -33,5 +43,20 @@ in {
bitcoin peer connections for syncing blocks. This performs well on low and high
memory systems.
'')
];
] ++
# 0.0.59
(map mkSplitEnforceTorOption [
"clightning"
"lightning-loop"
"lightning-pool"
"liquid"
"lnd"
"spark-wallet"
"bitcoind"
]) ++
(map mkRenamedEnforceTorOption [
"btcpayserver"
"rtl"
"electrs"
]);
}

View File

@ -1,26 +1,42 @@
{ lib, config, ... }:
let
defaultTrue = lib.mkDefault true;
defaultEnableTorProxy = {
tor.proxy = defaultTrue;
tor.enforce = defaultTrue;
};
defaultEnforceTor = {
tor.enforce = defaultTrue;
};
in {
services.tor = {
enable = true;
client.enable = true;
};
# Use Tor for all outgoing connections
services = {
bitcoind.enforceTor = true;
clightning.enforceTor = true;
lnd.enforceTor = true;
lightning-loop.enforceTor = true;
liquidd.enforceTor = true;
electrs.enforceTor = true;
# Use Tor as a proxy for outgoing connections
# and restrict all connections to Tor
#
bitcoind = defaultEnableTorProxy;
clightning = defaultEnableTorProxy;
lnd = defaultEnableTorProxy;
lightning-loop = defaultEnableTorProxy;
liquidd = defaultEnableTorProxy;
# disable Tor enforcement until btcpayserver can fetch rates over Tor
# btcpayserver.enforceTor = true;
nbxplorer.enforceTor = true;
spark-wallet.enforceTor = true;
lightning-pool.enforceTor = true;
rtl.enforceTor = true;
# btcpayserver = defaultEnableTorProxy;
spark-wallet = defaultEnableTorProxy;
lightning-pool = defaultEnableTorProxy;
# These services don't make outgoing connections
# (or use Tor by default in case of joinmarket)
# but we restrict them to Tor just to be safe.
#
electrs = defaultEnforceTor;
nbxplorer = defaultEnforceTor;
rtl = defaultEnforceTor;
joinmarket = defaultEnforceTor;
joinmarket-ob-watcher = defaultEnforceTor;
};
# Add onion services for incoming connections

View File

@ -89,7 +89,7 @@ let
description = "Swagger API documentation server port.";
};
};
inherit (nbLib) enforceTor;
tor.enforce = nbLib.tor.enforce;
};
cfg = config.services.rtl;
@ -214,7 +214,7 @@ in {
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.dataDir;
} // nbLib.allowedIPAddresses cfg.enforceTor
} // nbLib.allowedIPAddresses cfg.tor.enforce
// nbLib.nodejs;
};

View File

@ -38,7 +38,7 @@ let
default = cfg.user;
description = "The group as which to run spark-wallet.";
};
inherit (nbLib) enforceTor;
tor = nbLib.tor;
};
cfg = config.services.spark-wallet;
@ -57,7 +57,7 @@ let
--ln-path '${clightning.networkDir}' \
--host ${cfg.address} --port ${toString cfg.port} \
--config '${config.nix-bitcoin.secretsDir}/spark-wallet-login' \
${optionalString cfg.enforceTor torRateProvider} \
${optionalString cfg.tor.proxy torRateProvider} \
$publicURL \
--pairing-qr --print-key ${cfg.extraArgs}
'';
@ -76,7 +76,7 @@ in {
User = cfg.user;
Restart = "on-failure";
RestartSec = "10s";
} // nbLib.allowedIPAddresses cfg.enforceTor
} // nbLib.allowedIPAddresses cfg.tor.enforce
// nbLib.nodejs;
};

View File

@ -55,14 +55,21 @@ let self = {
then self.allowLocalIPAddresses
else self.allowAllIPAddresses;
enforceTor = mkOption {
tor = {
proxy = mkOption {
type = types.bool;
default = false;
description = "Whether to proxy outgoing connections with Tor.";
};
enforce = mkOption {
type = types.bool;
default = false;
description = ''
Whether to force Tor on a service by only allowing connections from and
to 127.0.0.1;
Whether to enforce Tor on a service by only allowing connections
from and to localhost and link-local addresses.
'';
};
};
script = name: src: pkgs.writers.writeBash name ''
set -eo pipefail