Merge #316: lndconnect: add preconfigured lndconnect-rest script

891a69ee8e docs: add zeus usage instructions (nixbitcoin)
ebd478fd0d lnd: add option 'restOnionService' (nixbitcoin)
a344ae95c9 move mkHiddenService to lib (Erik Arvstedt)
a26ed03d77 rename nix-bitcoin-services.nix -> lib.nix (Erik Arvstedt)

Pull request description:

ACKs for top commit:
  erikarvstedt:
    ACK 891a69ee8e

Tree-SHA512: 91bae39f92aed5bdd44499bf861c434b983b02e90fae317ee2f293df710cf101faecaabbe316821dc1a5b5bfb1db68195f05c9984d93e2d279c76c1cde061d95
This commit is contained in:
Jonas Nick 2021-02-05 22:09:40 +00:00
commit 9cd52e04b5
No known key found for this signature in database
GPG Key ID: 4861DBF262123605
23 changed files with 202 additions and 95 deletions

View File

@ -63,6 +63,7 @@ NixOS modules
* [summary](https://github.com/lightningd/plugins/tree/master/summary): print a nice summary of the node status * [summary](https://github.com/lightningd/plugins/tree/master/summary): print a nice summary of the node status
* [zmq](https://github.com/lightningd/plugins/tree/master/zmq): publishes notifications via ZeroMQ to configured endpoints * [zmq](https://github.com/lightningd/plugins/tree/master/zmq): publishes notifications via ZeroMQ to configured endpoints
* [lnd](https://github.com/lightningnetwork/lnd) with support for announcing an onion service * [lnd](https://github.com/lightningnetwork/lnd) with support for announcing an onion service
* [lndconnect](https://github.com/LN-Zap/lndconnect) via a REST onion service
* [spark-wallet](https://github.com/shesek/spark-wallet) * [spark-wallet](https://github.com/shesek/spark-wallet)
* [electrs](https://github.com/romanz/electrs) * [electrs](https://github.com/romanz/electrs)
* [btcpayserver](https://github.com/btcpayserver/btcpayserver) * [btcpayserver](https://github.com/btcpayserver/btcpayserver)
@ -82,7 +83,7 @@ Security
--- ---
* **Simplicity:** Only services you select in `configuration.nix` and their dependencies are installed, packages and dependencies are [pinned](pkgs/nixpkgs-pinned.nix), most packages are built from the [NixOS stable channel](https://github.com/NixOS/nixpkgs/tree/nixos-20.09), with a few exceptions that are built from the nixpkgs unstable channel, builds happen in a [sandboxed environment](https://nixos.org/manual/nix/stable/#conf-sandbox), code is continuously reviewed and refined. * **Simplicity:** Only services you select in `configuration.nix` and their dependencies are installed, packages and dependencies are [pinned](pkgs/nixpkgs-pinned.nix), most packages are built from the [NixOS stable channel](https://github.com/NixOS/nixpkgs/tree/nixos-20.09), with a few exceptions that are built from the nixpkgs unstable channel, builds happen in a [sandboxed environment](https://nixos.org/manual/nix/stable/#conf-sandbox), code is continuously reviewed and refined.
* **Integrity:** Nix package manager, NixOS and packages can be built from source to reduce reliance on binary caches, nix-bitcoin merge commits are signed, all commits are approved by multiple nix-bitcoin developers, upstream packages are cryptographically verified where possible, we use this software ourselves. * **Integrity:** Nix package manager, NixOS and packages can be built from source to reduce reliance on binary caches, nix-bitcoin merge commits are signed, all commits are approved by multiple nix-bitcoin developers, upstream packages are cryptographically verified where possible, we use this software ourselves.
* **Principle of Least Privilege:** Services operate with least privileges; they each have their own user and are restricted further with [systemd options](modules/nix-bitcoin-services.nix), [RPC whitelisting](modules/bitcoind-rpc-public-whitelist.nix), and [netns-isolation](modules/netns-isolation.nix). There's a non-root user *operator* to interact with the various services. * **Principle of Least Privilege:** Services operate with least privileges; they each have their own user and are restricted further with [systemd options](pkgs/lib.nix), [RPC whitelisting](modules/bitcoind-rpc-public-whitelist.nix), and [netns-isolation](modules/netns-isolation.nix). There's a non-root user *operator* to interact with the various services.
* **Defense-in-depth:** nix-bitcoin is built with a [hardened kernel](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix) by default, services are confined through discretionary access control, Linux namespaces, [dbus firewall](modules/security.nix) and seccomp-bpf with continuous improvements. * **Defense-in-depth:** nix-bitcoin is built with a [hardened kernel](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix) by default, services are confined through discretionary access control, Linux namespaces, [dbus firewall](modules/security.nix) and seccomp-bpf with continuous improvements.
Note that if the machine you're deploying *from* is insecure, there is nothing nix-bitcoin can do to protect itself. Note that if the machine you're deploying *from* is insecure, there is nothing nix-bitcoin can do to protect itself.

View File

@ -58,6 +58,47 @@ Connect to spark-wallet
Done Done
``` ```
Connect to LND with Zeus
---
### Requirements
* Android phone
* [Orbot](https://guardianproject.info/apps/orbot/) installed from
[F-Droid](https://guardianproject.info/fdroid) (recommended) or
[Google Play](https://play.google.com/store/apps/details?id=org.torproject.android&hl=en)
* [Zeus](https://zeusln.app/) installed from
[F-Droid](https://f-droid.org/en/packages/app.zeusln.zeus/) (recommended) or
[Google Play](https://play.google.com/store/apps/details?id=app.zeusln.zeus)
1. Enable `restOnionService` in `configuration.nix`
Change
```
# services.lnd.restOnionService.enable = true;
```
to
```
services.lnd.restOnionService.enable = true;
```
2. Deploy new `configuration.nix`
```
nixops deploy -d bitcoin-node
```
3. Run command `lndconnect-rest-onion` (under `operator` user) to create a QR code for
connecting to LND via the REST onion service.
4. Enable Orbot VPN for Zeus
```
Open Orbot app
Turn on "VPN Mode"
Select Gear icon under "Tor-Enabled Apps"
Toggle checkbox under Zeus icon
```
5. Scan the QR code with your Zeus wallet and start sending Satoshis privately
Connect to electrs Connect to electrs
--- ---
### Requirements Android ### Requirements Android

View File

@ -63,6 +63,12 @@
# The onion service is automatically announced to peers. # The onion service is automatically announced to peers.
# nix-bitcoin.onionServices.lnd.public = true; # nix-bitcoin.onionServices.lnd.public = true;
# #
# Set this to create an lnd REST onion service.
# Adds binary `lndconnect-rest-onion` to the system environment.
# This binary generates QR codes or URIs for connecting applications to lnd via the
# REST onion service.
# services.lnd.restOnionService.enable = true;
#
## WARNING ## WARNING
# If you use lnd, you should manually backup your wallet mnemonic # 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 # seed. This will allow you to recover on-chain funds. You can run the

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.bitcoind; cfg = config.services.bitcoind;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
configFile = pkgs.writeText "bitcoin.conf" '' configFile = pkgs.writeText "bitcoin.conf" ''
@ -291,7 +291,7 @@ in {
''; '';
description = "Binary to connect with the bitcoind instance."; description = "Binary to connect with the bitcoind instance.";
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
}; };
@ -348,7 +348,7 @@ in {
install -o '${cfg.user}' -g '${cfg.group}' -m 640 <(echo "$cfg") $confFile install -o '${cfg.user}' -g '${cfg.group}' -m 640 <(echo "$cfg") $confFile
fi fi
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
Type = "notify"; Type = "notify";
NotifyAccess = "all"; NotifyAccess = "all";
User = "${cfg.user}"; User = "${cfg.user}";
@ -359,9 +359,9 @@ in {
UMask = mkIf cfg.dataDirReadableByGroup "0027"; UMask = mkIf cfg.dataDirReadableByGroup "0027";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP) else nbLib.allowAnyIP)
// optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nix-bitcoin-services.allowAnyProtocol; // optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nbLib.allowAnyProtocol;
}; };
# Use this to update the banlist: # Use this to update the banlist:
@ -382,11 +382,11 @@ in {
fi fi
done done
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.group}"; Group = "${cfg.group}";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // nix-bitcoin-services.allowTor; } // nbLib.allowTor;
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services; cfg = config.services;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
in { in {
options.services = { options.services = {
@ -44,7 +44,7 @@ in {
internal = true; internal = true;
default = cfg.btcpayserver.enable; default = cfg.btcpayserver.enable;
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
btcpayserver = { btcpayserver = {
@ -90,7 +90,7 @@ in {
example = "btcpayserver"; example = "btcpayserver";
description = "The prefix for root-relative btcpayserver URLs."; description = "The prefix for root-relative btcpayserver URLs.";
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
}; };
@ -132,7 +132,7 @@ in {
echo "btcrpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-btcpayserver)" \ echo "btcrpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-btcpayserver)" \
>> '${cfg.nbxplorer.dataDir}/settings.config' >> '${cfg.nbxplorer.dataDir}/settings.config'
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStart = '' ExecStart = ''
${cfg.nbxplorer.package}/bin/nbxplorer --conf=${cfg.nbxplorer.dataDir}/settings.config \ ${cfg.nbxplorer.package}/bin/nbxplorer --conf=${cfg.nbxplorer.dataDir}/settings.config \
--datadir=${cfg.nbxplorer.dataDir} --datadir=${cfg.nbxplorer.dataDir}
@ -143,8 +143,8 @@ in {
ReadWritePaths = cfg.nbxplorer.dataDir; ReadWritePaths = cfg.nbxplorer.dataDir;
MemoryDenyWriteExecute = "false"; MemoryDenyWriteExecute = "false";
} // (if cfg.nbxplorer.enforceTor } // (if cfg.nbxplorer.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP else nbLib.allowAnyIP
); );
}; };
@ -181,7 +181,7 @@ in {
} >> ${cfg.btcpayserver.dataDir}/settings.config } >> ${cfg.btcpayserver.dataDir}/settings.config
''} ''}
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStart = '' ExecStart = ''
${cfg.btcpayserver.package}/bin/btcpayserver --conf=${cfg.btcpayserver.dataDir}/settings.config \ ${cfg.btcpayserver.package}/bin/btcpayserver --conf=${cfg.btcpayserver.dataDir}/settings.config \
--datadir=${cfg.btcpayserver.dataDir} --datadir=${cfg.btcpayserver.dataDir}
@ -192,8 +192,8 @@ in {
ReadWritePaths = cfg.btcpayserver.dataDir; ReadWritePaths = cfg.btcpayserver.dataDir;
MemoryDenyWriteExecute = "false"; MemoryDenyWriteExecute = "false";
} // (if cfg.btcpayserver.enforceTor } // (if cfg.btcpayserver.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP else nbLib.allowAnyIP
); );
}; in self; }; in self;

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.clightning; cfg = config.services.clightning;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
network = config.services.bitcoind.makeNetworkName "bitcoin" "regtest"; network = config.services.bitcoind.makeNetworkName "bitcoin" "regtest";
configFile = pkgs.writeText "config" '' configFile = pkgs.writeText "config" ''
@ -91,7 +91,7 @@ in {
If left empty, no address is announced. If left empty, no address is announced.
''; '';
}; };
inherit (nix-bitcoin-services) enforceTor; inherit (nbLib) enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -135,15 +135,15 @@ in {
} >> '${cfg.dataDir}/config' } >> '${cfg.dataDir}/config'
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; ExecStart = "${nbPkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
User = "${cfg.user}"; User = "${cfg.user}";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP else nbLib.allowAnyIP
); );
# Wait until the rpc socket appears # Wait until the rpc socket appears
postStart = '' postStart = ''

View File

@ -3,7 +3,7 @@
with lib; with lib;
let let
cfg = config.services.electrs; cfg = config.services.electrs;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
bitcoind = config.services.bitcoind; bitcoind = config.services.bitcoind;
in { in {
@ -51,7 +51,7 @@ in {
default = ""; default = "";
description = "Extra command line arguments passed to electrs."; description = "Extra command line arguments passed to electrs.";
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -76,7 +76,7 @@ in {
echo "cookie = \"${bitcoind.rpc.users.public.name}:$(cat ${secretsDir}/bitcoin-rpcpassword-public)\"" \ echo "cookie = \"${bitcoind.rpc.users.public.name}:$(cat ${secretsDir}/bitcoin-rpcpassword-public)\"" \
> electrs.toml > electrs.toml
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
RuntimeDirectory = "electrs"; RuntimeDirectory = "electrs";
RuntimeDirectoryMode = "700"; RuntimeDirectoryMode = "700";
WorkingDirectory = "/run/electrs"; WorkingDirectory = "/run/electrs";
@ -104,8 +104,8 @@ in {
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir} ${if cfg.high-memory then "${bitcoind.dataDir}" else ""}"; ReadWritePaths = "${cfg.dataDir} ${if cfg.high-memory then "${bitcoind.dataDir}" else ""}";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP else nbLib.allowAnyIP
); );
}; };

View File

@ -3,7 +3,7 @@
with lib; with lib;
let let
cfg = config.services.joinmarket-ob-watcher; cfg = config.services.joinmarket-ob-watcher;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
torAddress = builtins.head (builtins.split ":" config.services.tor.client.socksListenAddress); torAddress = builtins.head (builtins.split ":" config.services.tor.client.socksListenAddress);
configFile = builtins.toFile "config" '' configFile = builtins.toFile "config" ''
@ -76,7 +76,7 @@ in {
preStart = '' preStart = ''
ln -snf ${configFile} ${cfg.dataDir}/joinmarket.cfg ln -snf ${configFile} ${cfg.dataDir}/joinmarket.cfg
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // rec { serviceConfig = nbLib.defaultHardening // rec {
StateDirectory = "joinmarket-ob-watcher"; StateDirectory = "joinmarket-ob-watcher";
StateDirectoryMode = "0770"; StateDirectoryMode = "0770";
WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir
@ -87,7 +87,7 @@ in {
User = cfg.user; User = cfg.user;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.allowTor; } // nbLib.allowTor;
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.joinmarket; cfg = config.services.joinmarket;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
@ -137,7 +137,7 @@ in {
readOnly = true; readOnly = true;
default = true; default = true;
}; };
inherit (nix-bitcoin-services) cliExec; inherit (nbLib) cliExec;
}; };
config = mkIf cfg.enable (mkMerge [{ config = mkIf cfg.enable (mkMerge [{
@ -177,15 +177,15 @@ in {
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
path = [ pkgs.sudo ]; path = [ pkgs.sudo ];
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStartPre = nix-bitcoin-services.privileged '' ExecStartPre = nbLib.privileged ''
install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg install -o '${cfg.user}' -g '${cfg.group}' -m 640 ${configFile} ${cfg.dataDir}/joinmarket.cfg
sed -i \ sed -i \
"s|@@RPC_PASSWORD@@|rpc_password = $(cat ${secretsDir}/bitcoin-rpcpassword-privileged)|" \ "s|@@RPC_PASSWORD@@|rpc_password = $(cat ${secretsDir}/bitcoin-rpcpassword-privileged)|" \
'${cfg.dataDir}/joinmarket.cfg' '${cfg.dataDir}/joinmarket.cfg'
''; '';
# Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet # Generating wallets (jmclient/wallet.py) is only supported for mainnet or testnet
ExecStartPost = mkIf (bitcoind.network == "mainnet") (nix-bitcoin-services.privileged '' ExecStartPost = mkIf (bitcoind.network == "mainnet") (nbLib.privileged ''
walletname=wallet.jmdat walletname=wallet.jmdat
pw=$(cat "${secretsDir}"/jm-wallet-password) pw=$(cat "${secretsDir}"/jm-wallet-password)
mnemonic=${secretsDir}/jm-wallet-seed mnemonic=${secretsDir}/jm-wallet-seed
@ -207,7 +207,7 @@ in {
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // nix-bitcoin-services.allowTor; } // nbLib.allowTor;
}; };
nix-bitcoin.secrets.jm-wallet-password.user = cfg.user; nix-bitcoin.secrets.jm-wallet-password.user = cfg.user;
@ -239,14 +239,14 @@ in {
pw=$(cat "${secretsDir}"/jm-wallet-password) pw=$(cat "${secretsDir}"/jm-wallet-password)
echo "echo -n $pw | ${start}" > $RUNTIME_DIRECTORY/start echo "echo -n $pw | ${start}" > $RUNTIME_DIRECTORY/start
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // rec { serviceConfig = nbLib.defaultHardening // rec {
RuntimeDirectory = "joinmarket-yieldgenerator"; # Only used to create start script RuntimeDirectory = "joinmarket-yieldgenerator"; # Only used to create start script
RuntimeDirectoryMode = "700"; RuntimeDirectoryMode = "700";
WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir WorkingDirectory = "${cfg.dataDir}"; # The service creates dir 'logs' in the working dir
ExecStart = "${pkgs.bash}/bin/bash /run/${RuntimeDirectory}/start"; ExecStart = "${pkgs.bash}/bin/bash /run/${RuntimeDirectory}/start";
User = "${cfg.user}"; User = "${cfg.user}";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // nix-bitcoin-services.allowTor; } // nbLib.allowTor;
}; };
}) })
]); ]);

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.lightning-loop; cfg = config.services.lightning-loop;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
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}"; rpclisten = "${cfg.rpcAddress}:${toString cfg.rpcPort}";
@ -80,7 +80,7 @@ in {
''; '';
description = "Binary to connect with the lightning-loop instance."; description = "Binary to connect with the lightning-loop instance.";
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -96,15 +96,15 @@ in {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "lnd.service" ]; requires = [ "lnd.service" ];
after = [ "lnd.service" ]; after = [ "lnd.service" ];
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStart = "${cfg.package}/bin/loopd --configfile=${configFile}"; ExecStart = "${cfg.package}/bin/loopd --configfile=${configFile}";
User = "lnd"; User = "lnd";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP); else nbLib.allowAnyIP);
}; };
nix-bitcoin.secrets = { nix-bitcoin.secrets = {

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.liquidd; cfg = config.services.liquidd;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
nbPkgs = config.nix-bitcoin.pkgs; nbPkgs = config.nix-bitcoin.pkgs;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
pidFile = "${cfg.dataDir}/liquidd.pid"; pidFile = "${cfg.dataDir}/liquidd.pid";
@ -203,7 +203,7 @@ in {
''; '';
description = "Binary for managing liquid swaps."; description = "Binary for managing liquid swaps.";
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
}; };
@ -232,7 +232,7 @@ in {
echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf' echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf'
echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword-public)" >> '${cfg.dataDir}/elements.conf' echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword-public)" >> '${cfg.dataDir}/elements.conf'
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
Type = "simple"; Type = "simple";
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.group}"; Group = "${cfg.group}";
@ -241,8 +241,8 @@ in {
Restart = "on-failure"; Restart = "on-failure";
ReadWritePaths = "${cfg.dataDir}"; ReadWritePaths = "${cfg.dataDir}";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP else nbLib.allowAnyIP
); );
}; };

View File

@ -0,0 +1,51 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.lnd.restOnionService;
nbLib = config.nix-bitcoin.lib;
secretsDir = config.nix-bitcoin.secretsDir;
lnd = config.services.lnd;
bin = pkgs.writeScriptBin "lndconnect-rest-onion" ''
#!/usr/bin/env -S sudo -u lnd ${pkgs.bash}/bin/bash
exec ${cfg.package}/bin/lndconnect \
--host=$(cat ${config.nix-bitcoin.onionAddresses.dataDir}/lnd/lnd-rest) \
--port=${toString lnd.restPort} \
--lnddir=${lnd.dataDir} \
--tlscertpath=${secretsDir}/lnd-cert "$@"
'';
in {
options.services.lnd.restOnionService = {
enable = mkOption {
default = false;
type = types.bool;
description = ''
Create an onion service for the lnd REST service.
Add a `lndconnect-rest-onion` binary (https://github.com/LN-Zap/lndconnect) to the system environment.
This binary generates QR codes or URIs for connecting applications to lnd via the REST onion service.
'';
};
package = mkOption {
type = types.package;
default = config.nix-bitcoin.pkgs.lndconnect;
description = "The package providing lndconnect binaries.";
};
};
config = mkIf cfg.enable {
services.tor = {
enable = true;
hiddenServices.lnd-rest = nbLib.mkHiddenService {
toHost = lnd.restAddress;
port = lnd.restPort;
};
};
nix-bitcoin.onionAddresses.access.lnd = [ "lnd-rest" ];
environment.systemPackages = [ bin ];
};
}

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.lnd; cfg = config.services.lnd;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
secretsDir = config.nix-bitcoin.secretsDir; secretsDir = config.nix-bitcoin.secretsDir;
bitcoind = config.services.bitcoind; bitcoind = config.services.bitcoind;
@ -144,7 +144,7 @@ in {
If left empty, no address is announced. If left empty, no address is announced.
''; '';
}; };
inherit (nix-bitcoin-services) enforceTor; inherit (nbLib) enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -186,7 +186,7 @@ in {
''} ''}
} >> '${cfg.dataDir}/lnd.conf' } >> '${cfg.dataDir}/lnd.conf'
''; '';
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
RuntimeDirectory = "lnd"; # Only used to store custom macaroons RuntimeDirectory = "lnd"; # Only used to store custom macaroons
RuntimeDirectoryMode = "711"; RuntimeDirectoryMode = "711";
ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf"; ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf";
@ -198,7 +198,7 @@ in {
restUrl = "https://${cfg.restAddress}:${toString cfg.restPort}/v1"; restUrl = "https://${cfg.restAddress}:${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 '' "+${nbLib.script ''
attempts=250 attempts=250
while ! { exec 3>/dev/tcp/${cfg.restAddress}/${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; } ((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
@ -215,7 +215,7 @@ in {
fi fi
chown lnd: "$mnemonic" chown lnd: "$mnemonic"
''}" ''}"
"${nix-bitcoin-services.script '' "${nbLib.script ''
if [[ ! -f ${networkDir}/wallet.db ]]; then if [[ ! -f ${networkDir}/wallet.db ]]; then
echo Create lnd wallet echo Create lnd wallet
@ -248,7 +248,7 @@ in {
''}" ''}"
# Run fully privileged for chown # Run fully privileged for chown
"+${nix-bitcoin-services.script '' "+${nbLib.script ''
umask ug=r,o= umask ug=r,o=
${lib.concatMapStrings (macaroon: '' ${lib.concatMapStrings (macaroon: ''
echo "Create custom macaroon ${macaroon}" echo "Create custom macaroon ${macaroon}"
@ -265,9 +265,9 @@ in {
''}" ''}"
]; ];
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP else nbLib.allowAnyIP
) // nix-bitcoin-services.allowAnyProtocol; # For ZMQ ) // nbLib.allowAnyProtocol; # For ZMQ
}; };
users.users.lnd = { users.users.lnd = {

View File

@ -13,6 +13,7 @@ with lib;
./clightning-plugins ./clightning-plugins
./spark-wallet.nix ./spark-wallet.nix
./lnd.nix ./lnd.nix
./lnd-rest-onion-service.nix
./lightning-loop.nix ./lightning-loop.nix
./btcpayserver.nix ./btcpayserver.nix
./electrs.nix ./electrs.nix
@ -35,17 +36,17 @@ with lib;
disabledModules = [ "services/networking/bitcoind.nix" ]; disabledModules = [ "services/networking/bitcoind.nix" ];
options = { options = {
nix-bitcoin-services = mkOption {
readOnly = true;
default = import ./nix-bitcoin-services.nix lib pkgs;
};
nix-bitcoin = { nix-bitcoin = {
pkgs = mkOption { pkgs = mkOption {
type = types.attrs; type = types.attrs;
default = (import ../pkgs { inherit pkgs; }).modulesPkgs; default = (import ../pkgs { inherit pkgs; }).modulesPkgs;
}; };
lib = mkOption {
readOnly = true;
default = import ../pkgs/lib.nix lib pkgs;
};
# Torify binary that works with custom Tor SOCKS addresses # Torify binary that works with custom Tor SOCKS addresses
# Related issue: https://github.com/NixOS/nixpkgs/issues/94236 # Related issue: https://github.com/NixOS/nixpkgs/issues/94236
torify = mkOption { torify = mkOption {

View File

@ -10,7 +10,7 @@ with lib;
let let
cfg = config.nix-bitcoin.onionAddresses; cfg = config.nix-bitcoin.onionAddresses;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
in { in {
options.nix-bitcoin.onionAddresses = { options.nix-bitcoin.onionAddresses = {
access = mkOption { access = mkOption {
@ -47,7 +47,7 @@ in {
wantedBy = [ "tor.service" ]; wantedBy = [ "tor.service" ];
bindsTo = [ "tor.service" ]; bindsTo = [ "tor.service" ];
after = [ "tor.service" ]; after = [ "tor.service" ];
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
StateDirectory = "onion-addresses"; StateDirectory = "onion-addresses";

View File

@ -10,6 +10,7 @@ with lib;
let let
cfg = config.nix-bitcoin.onionServices; cfg = config.nix-bitcoin.onionServices;
nbLib = config.nix-bitcoin.lib;
services = builtins.attrNames cfg; services = builtins.attrNames cfg;
@ -60,13 +61,10 @@ in {
let let
service = config.services.${name}; service = config.services.${name};
inherit (cfg.${name}) externalPort; inherit (cfg.${name}) externalPort;
in { in nbLib.mkHiddenService {
map = [{ port = if externalPort != null then externalPort else service.port;
port = if externalPort != null then externalPort else service.port; toPort = service.port;
toPort = service.port; toHost = if service.address == "0.0.0.0" then "127.0.0.1" else service.address;
toHost = if service.address == "0.0.0.0" then "127.0.0.1" else service.address;
}];
version = 3;
} }
); );
}; };

View File

@ -4,13 +4,8 @@ with lib;
let let
cfg = config.services; cfg = config.services;
nbLib = config.nix-bitcoin.lib;
operatorName = config.nix-bitcoin.operator.name; operatorName = config.nix-bitcoin.operator.name;
mkHiddenService = map: {
map = [ map ];
version = 3;
};
in { in {
imports = [ imports = [
../modules.nix ../modules.nix
@ -30,7 +25,7 @@ in {
]; ];
# sshd # sshd
services.tor.hiddenServices.sshd = mkHiddenService { port = 22; }; services.tor.hiddenServices.sshd = nbLib.mkHiddenService { port = 22; };
nix-bitcoin.onionAddresses.access.${operatorName} = [ "sshd" ]; nix-bitcoin.onionAddresses.access.${operatorName} = [ "sshd" ];
services.bitcoind = { services.bitcoind = {

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.recurring-donations; cfg = config.services.recurring-donations;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
recurring-donations-script = pkgs.writeScript "recurring-donations.sh" '' recurring-donations-script = pkgs.writeScript "recurring-donations.sh" ''
LNCLI="${config.nix-bitcoin.pkgs.clightning}/bin/lightning-cli --lightning-dir=${config.services.clightning.dataDir}" LNCLI="${config.nix-bitcoin.pkgs.clightning}/bin/lightning-cli --lightning-dir=${config.services.clightning.dataDir}"
pay_tallycoin() { pay_tallycoin() {
@ -75,7 +75,7 @@ in {
Random delay to add to scheduled time for donation. Default is one day. Random delay to add to scheduled time for donation. Default is one day.
''; '';
}; };
enforceTor = nix-bitcoin-services.enforceTor; enforceTor = nbLib.enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -93,13 +93,13 @@ in {
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];
after = [ "clightning.service" ]; after = [ "clightning.service" ];
path = with pkgs; [ nix-bitcoin.clightning curl sudo jq ]; path = with pkgs; [ nix-bitcoin.clightning curl sudo jq ];
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
ExecStart = "${pkgs.bash}/bin/bash ${recurring-donations-script}"; ExecStart = "${pkgs.bash}/bin/bash ${recurring-donations-script}";
User = "recurring-donations"; User = "recurring-donations";
Type = "oneshot"; Type = "oneshot";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP); else nbLib.allowAnyIP);
}; };
systemd.timers.recurring-donations = { systemd.timers.recurring-donations = {
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];

View File

@ -4,7 +4,7 @@ with lib;
let let
cfg = config.services.spark-wallet; cfg = config.services.spark-wallet;
inherit (config) nix-bitcoin-services; nbLib = config.nix-bitcoin.lib;
# Use wasabi rate provider because the default (bitstamp) doesn't accept # Use wasabi rate provider because the default (bitstamp) doesn't accept
# connections through Tor # connections through Tor
@ -54,7 +54,7 @@ in {
encodes an URL for accessing the web interface. encodes an URL for accessing the web interface.
''; '';
}; };
inherit (nix-bitcoin-services) enforceTor; inherit (nbLib) enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -73,14 +73,14 @@ in {
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];
after = [ "clightning.service" ]; after = [ "clightning.service" ];
script = startScript; script = startScript;
serviceConfig = nix-bitcoin-services.defaultHardening // { serviceConfig = nbLib.defaultHardening // {
User = "spark-wallet"; User = "spark-wallet";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nbLib.allowTor
else nix-bitcoin-services.allowAnyIP) else nbLib.allowAnyIP)
// nix-bitcoin-services.nodejs; // nbLib.nodejs;
}; };
nix-bitcoin.secrets.spark-wallet-login.user = "spark-wallet"; nix-bitcoin.secrets.spark-wallet-login.user = "spark-wallet";
}; };

View File

@ -1,9 +1,9 @@
# See `man systemd.exec` and `man systemd.resource-control` for an explanation
# of the various systemd options available through this module.
lib: pkgs: lib: pkgs:
with lib; with lib;
# See `man systemd.exec` and `man systemd.resource-control` for an explanation
# of the systemd-related options available through this module.
let self = { let self = {
# These settings roughly follow systemd's "strict" security profile # These settings roughly follow systemd's "strict" security profile
defaultHardening = { defaultHardening = {
@ -46,8 +46,8 @@ let self = {
type = types.bool; type = types.bool;
default = false; default = false;
description = '' description = ''
"Whether to force Tor on a service by only allowing connections from and Whether to force Tor on a service by only allowing connections from and
to 127.0.0.1;"; to 127.0.0.1;
''; '';
}; };
@ -65,4 +65,9 @@ let self = {
type = types.str; type = types.str;
default = "exec"; default = "exec";
}; };
mkHiddenService = map: {
map = [ map ];
version = 3;
};
}; in self }; in self

View File

@ -11,6 +11,7 @@ in
bitcoind bitcoind
clightning clightning
lnd lnd
lndconnect
nbxplorer nbxplorer
btcpayserver; btcpayserver;

View File

@ -46,6 +46,8 @@ let testEnv = rec {
tests.lnd = cfg.lnd.enable; tests.lnd = cfg.lnd.enable;
services.lnd.port = 9736; services.lnd.port = 9736;
tests.lnd-rest-onion-service = cfg.lnd.restOnionService.enable;
tests.lightning-loop = cfg.lightning-loop.enable; tests.lightning-loop = cfg.lightning-loop.enable;
tests.electrs = cfg.electrs.enable; tests.electrs = cfg.electrs.enable;
@ -115,6 +117,7 @@ let testEnv = rec {
test.features.clightningPlugins = true; test.features.clightningPlugins = true;
services.spark-wallet.enable = true; services.spark-wallet.enable = true;
services.lnd.enable = true; services.lnd.enable = true;
services.lnd.restOnionService.enable = true;
services.lightning-loop.enable = true; services.lightning-loop.enable = true;
services.electrs.enable = true; services.electrs.enable = true;
services.liquidd.enable = true; services.liquidd.enable = true;

View File

@ -162,6 +162,11 @@ def _():
assert_no_failure("lnd") assert_no_failure("lnd")
@test("lnd-rest-onion-service")
def _():
assert_matches("runuser -u operator -- lndconnect-rest-onion -j", ".onion")
@test("lightning-loop") @test("lightning-loop")
def _(): def _():
assert_running("lightning-loop") assert_running("lightning-loop")