Skip to content

Commit

Permalink
Merge #221: nbxplorer/btcpayserver: add module
Browse files Browse the repository at this point in the history
c9c844d btcpayserver: add tests (nixbitcoin)
f93c3c8 backups: add nbxplorer and btcpayserver datadir to includelist (nixbitcoin)
605b37c nodeinfo: add btcpayserver onion (nixbitcoin)
15b574f nbxplorer/btcpayserver: add module (nixbitcoin)
46d681a lnd: generate custom macaroons (nixbitcoin)
6f032e3 lnd: fix mnemonic file access vulnerability (Erik Arvstedt)
b97584f netns: allow return traffic to outgoing connections (nixbitcoin)
9929532 temp: mirror erikarvstedt btcpayserver (Calvin Kim)

Pull request description:

ACKs for top commit:
  erikarvstedt:
    ACK c9c844d

Tree-SHA512: 0020964db37f5c5db3343ddef88f2e7e8d8ad48760ece73125fd9d2feaed0a3789ba3fd3eff98c225a675b49447b1728cd2c9eb4fa495c961e8376b28d32bad9
  • Loading branch information
jonasnick committed Sep 15, 2020
2 parents adae7da + c9c844d commit a9c163c
Show file tree
Hide file tree
Showing 24 changed files with 3,600 additions and 9 deletions.
4 changes: 4 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ env:
- PKG=elementsd STABLE=0
- PKG=electrs STABLE=1
- PKG=electrs STABLE=0
- PKG=nbxplorer STABLE=1
- PKG=nbxplorer STABLE=0
- PKG=btcpayserver STABLE=1
- PKG=btcpayserver STABLE=0
- PKG=liquid-swap STABLE=1
- PKG=lightning-loop STABLE=0
- PKG=nixops19_09 STABLE=1
Expand Down
14 changes: 14 additions & 0 deletions examples/configuration.nix
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@
# sync faster. Only available if hardware wallets are disabled.
# services.electrs.high-memory = true;

### BTCPayServer
# Enable this module to use BTCPayServer, a self-hosted, open-source
# cryptocurrency payment processor.
# Privacy Warning: BTCPayServer currently looks up price rates without
# proxying them through Tor. This means an outside observer can correlate
# your BTCPayServer usage, like invoice creation times, with your IP address.
# services.btcpayserver.enable = true;
# Enable this option to connect BTCPayServer to clightning.
# services.btcpayserver.lightningBackend = "clightning";
# Enable this option to connect BTCPayServert to lnd.
# services.btcpayserver.lightningBackend = "lnd";
# Afterwards you need to go into Store > General Settings > Lightning Nodes
# and click to use "the internal lightning node of this BTCPay Server".

### LIQUIDD
# Enable this module to use Liquid, a sidechain for an inter-exchange
# settlement network linking together cryptocurrency exchanges and
Expand Down
2 changes: 2 additions & 0 deletions modules/backups.nix
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ let
${config.services.liquidd.dataDir}
${optionalString cfg.with-bulk-data "${config.services.electrs.dataDir}"}
${config.services.lightning-charge.dataDir}
${config.services.nbxplorer.dataDir}
${config.services.btcpayserver.dataDir}
/var/lib/tor
# Extra files
${cfg.extraFiles}
Expand Down
211 changes: 211 additions & 0 deletions modules/btcpayserver.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
{ config, lib, pkgs, ... }:

with lib;

let
cfg = config.services;
inherit (config) nix-bitcoin-services;
in {
options.services = {
nbxplorer = {
package = mkOption {
type = types.package;
default = pkgs.nix-bitcoin.nbxplorer;
defaultText = "pkgs.nix-bitcoin.nbxplorer";
description = "The package providing nbxplorer binaries.";
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/nbxplorer";
description = "The data directory for nbxplorer.";
};
user = mkOption {
type = types.str;
default = "nbxplorer";
description = "The user as which to run nbxplorer.";
};
group = mkOption {
type = types.str;
default = cfg.nbxplorer.user;
description = "The group as which to run nbxplorer.";
};
bind = mkOption {
type = types.str;
default = "127.0.0.1";
description = "The address on which to bind.";
};
enable = mkOption {
# This option is only used by netns-isolation
internal = true;
default = cfg.btcpayserver.enable;
};
enforceTor = nix-bitcoin-services.enforceTor;
};

btcpayserver = {
enable = mkEnableOption "btcpayserver";
package = mkOption {
type = types.package;
default = pkgs.nix-bitcoin.btcpayserver;
defaultText = "pkgs.nix-bitcoin.btcpayserver";
description = "The package providing btcpayserver binaries.";
};
dataDir = mkOption {
type = types.path;
default = "/var/lib/btcpayserver";
description = "The data directory for btcpayserver.";
};
user = mkOption {
type = types.str;
default = "btcpayserver";
description = "The user as which to run btcpayserver.";
};
group = mkOption {
type = types.str;
default = cfg.btcpayserver.user;
description = "The group as which to run btcpayserver.";
};
bind = mkOption {
type = types.str;
default = "127.0.0.1";
description = "The address on which to bind.";
};
lightningBackend = mkOption {
type = types.nullOr (types.enum [ "clightning" "lnd" ]);
default = null;
description = "The lightning node implementation to use.";
};
enforceTor = nix-bitcoin-services.enforceTor;
};
};

config = mkIf cfg.btcpayserver.enable {
assertions = let
backend = cfg.btcpayserver.lightningBackend;
in [
{ assertion = (backend != null) -> cfg.${backend}.enable;
message = "btcpayserver requires ${backend}.";
}
];

systemd.tmpfiles.rules = [
"d '${cfg.nbxplorer.dataDir}' 0770 ${cfg.nbxplorer.user} ${cfg.nbxplorer.group} - -"
"d '${cfg.btcpayserver.dataDir}' 0770 ${cfg.btcpayserver.user} ${cfg.btcpayserver.group} - -"
];

systemd.services.nbxplorer = let
configFile = builtins.toFile "config" ''
network=mainnet
btcrpcuser=${cfg.bitcoind.rpc.users.btcpayserver.name}
btcrpcurl=http://${builtins.elemAt config.services.bitcoind.rpcbind 0}:8332
btcnodeendpoint=${config.services.bitcoind.bind}:8333
bind=${cfg.nbxplorer.bind}
'';
in {
description = "Run nbxplorer";
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
preStart = ''
install -m 600 ${configFile} ${cfg.nbxplorer.dataDir}/settings.config
echo "btcrpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword-btcpayserver)" \
>> '${cfg.nbxplorer.dataDir}/settings.config'
'';
serviceConfig = nix-bitcoin-services.defaultHardening // {
ExecStart = ''
${cfg.nbxplorer.package}/bin/nbxplorer --conf=${cfg.nbxplorer.dataDir}/settings.config \
--datadir=${cfg.nbxplorer.dataDir}
'';
User = cfg.nbxplorer.user;
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.nbxplorer.dataDir;
MemoryDenyWriteExecute = "false";
} // (if cfg.nbxplorer.enforceTor
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
);
};

systemd.services.btcpayserver = let
configFile = builtins.toFile "config" (''
network=mainnet
socksendpoint=${cfg.tor.client.socksListenAddress}
btcexplorerurl=http://${cfg.nbxplorer.bind}:24444/
btcexplorercookiefile=${cfg.nbxplorer.dataDir}/Main/.cookie
bind=${cfg.btcpayserver.bind}
'' + optionalString (cfg.btcpayserver.lightningBackend == "clightning") ''
btclightning=type=clightning;server=unix:///${cfg.clightning.dataDir}/bitcoin/lightning-rpc
'');
lndConfig =
"btclightning=type=lnd-rest;" +
"server=https://${toString cfg.lnd.listen}:${toString cfg.lnd.restPort}/;" +
"macaroonfilepath=/run/lnd/btcpayserver.macaroon;" +
"certthumbprint=";
in let self = {
wantedBy = [ "multi-user.target" ];
requires = [ "nbxplorer.service" ]
++ optional (cfg.btcpayserver.lightningBackend != null) "${cfg.btcpayserver.lightningBackend}.service";
after = self.requires;
preStart = ''
install -m 600 ${configFile} ${cfg.btcpayserver.dataDir}/settings.config
${optionalString (cfg.btcpayserver.lightningBackend == "lnd") ''
{
echo -n "${lndConfig}";
${pkgs.openssl}/bin/openssl x509 -noout -fingerprint -sha256 -in ${config.nix-bitcoin.secretsDir}/lnd-cert \
| sed -e 's/.*=//;s/://g';
} >> ${cfg.btcpayserver.dataDir}/settings.config
''}
'';
serviceConfig = nix-bitcoin-services.defaultHardening // {
ExecStart = ''
${cfg.btcpayserver.package}/bin/btcpayserver --conf=${cfg.btcpayserver.dataDir}/settings.config \
--datadir=${cfg.btcpayserver.dataDir}
'';
User = cfg.btcpayserver.user;
Restart = "on-failure";
RestartSec = "10s";
ReadWritePaths = cfg.btcpayserver.dataDir;
MemoryDenyWriteExecute = "false";
} // (if cfg.btcpayserver.enforceTor
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
);
}; in self;

services.lnd.macaroons.btcpayserver = mkIf (cfg.btcpayserver.lightningBackend == "lnd") {
inherit (cfg.btcpayserver) user;
permissions = ''{"entity":"info","action":"read"},{"entity":"onchain","action":"read"},{"entity":"offchain","action":"read"},{"entity":"address","action":"read"},{"entity":"message","action":"read"},{"entity":"peers","action":"read"},{"entity":"signer","action":"read"},{"entity":"invoices","action":"read"},{"entity":"invoices","action":"write"},{"entity":"address","action":"write"}'';
};

users.users.${cfg.nbxplorer.user} = {
description = "nbxplorer user";
group = cfg.nbxplorer.group;
extraGroups = [ "bitcoinrpc" ];
home = cfg.nbxplorer.dataDir;
};
users.groups.${cfg.nbxplorer.group} = {};
users.users.${cfg.btcpayserver.user} = {
description = "btcpayserver user";
group = cfg.btcpayserver.group;
extraGroups = [ "nbxplorer" ]
++ optional (cfg.btcpayserver.lightningBackend == "clightning") cfg.clightning.user;
home = cfg.btcpayserver.dataDir;
};
users.groups.${cfg.btcpayserver.group} = {};

services.bitcoind.rpc.users.btcpayserver = {
passwordHMACFromFile = true;
rpcwhitelist = cfg.bitcoind.rpc.users.public.rpcwhitelist ++ [
"setban"
"generatetoaddress"
"getpeerinfo"
];
};
nix-bitcoin.secrets.bitcoin-rpcpassword-btcpayserver = {
user = "bitcoin";
group = "nbxplorer";
};
nix-bitcoin.secrets.bitcoin-HMAC-btcpayserver.user = "bitcoin";
};
}
49 changes: 44 additions & 5 deletions modules/lnd.nix
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let
inherit (config) nix-bitcoin-services;
onion-chef-service = (if cfg.announce-tor then [ "onion-chef.service" ] else []);
secretsDir = config.nix-bitcoin.secretsDir;
mainnetDir = "${cfg.dataDir}/chain/bitcoin/mainnet";
configFile = pkgs.writeText "lnd.conf" ''
datadir=${cfg.dataDir}
logdir=${cfg.dataDir}/logs
Expand Down Expand Up @@ -97,6 +98,27 @@ in {
default = false;
description = "Announce LND Tor Hidden Service";
};
macaroons = mkOption {
default = {};
type = with types; attrsOf (submodule {
options = {
user = mkOption {
type = types.str;
description = "User who owns the macaroon.";
};
permissions = mkOption {
type = types.str;
example = ''
{"entity":"info","action":"read"},{"entity":"onchain","action":"read"}
'';
description = "List of granted macaroon permissions.";
};
};
});
description = ''
Extra macaroon definitions.
'';
};
extraConfig = mkOption {
type = types.lines;
default = "";
Expand Down Expand Up @@ -155,6 +177,8 @@ in {
${optionalString cfg.announce-tor "echo externalip=$(cat /var/lib/onion-chef/lnd/lnd) >> '${cfg.dataDir}/lnd.conf'"}
'';
serviceConfig = nix-bitcoin-services.defaultHardening // {
RuntimeDirectory = "lnd"; # Only used to store custom macaroons
RuntimeDirectoryMode = "711";
ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf";
User = "lnd";
Restart = "on-failure";
Expand All @@ -174,17 +198,14 @@ in {
mnemonic=${secretsDir}/lnd-seed-mnemonic
if [[ ! -f $mnemonic ]]; then
echo Create lnd seed
umask u=r,go=
${pkgs.curl}/bin/curl -s \
--cacert ${secretsDir}/lnd-cert \
-X GET https://127.0.0.1:${restPort}/v1/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > "$mnemonic"
fi
chown lnd: "$mnemonic"
chmod 400 "$mnemonic"
''}"
"${let
mainnetDir = "${cfg.dataDir}/chain/bitcoin/mainnet";
in nix-bitcoin-services.script ''
"${nix-bitcoin-services.script ''
if [[ ! -f ${mainnetDir}/wallet.db ]]; then
echo Create lnd wallet
Expand Down Expand Up @@ -214,6 +235,23 @@ in {
while ! { exec 3>/dev/tcp/127.0.0.1/${toString cfg.rpcPort}; } &>/dev/null; do
sleep 0.1
done
''}"
# Run fully privileged for chown
"+${nix-bitcoin-services.script ''
umask ug=r,o=
${lib.concatMapStrings (macaroon: ''
echo "Create custom macaroon ${macaroon}"
macaroonPath="$RUNTIME_DIRECTORY/${macaroon}.macaroon"
${pkgs.curl}/bin/curl -s \
-H "Grpc-Metadata-macaroon: $(${pkgs.xxd}/bin/xxd -ps -u -c 99999 '${mainnetDir}/admin.macaroon')" \
--cacert ${secretsDir}/lnd-cert \
-X POST \
-d '{"permissions":[${cfg.macaroons.${macaroon}.permissions}]}' \
https://127.0.0.1:${restPort}/v1/macaroon |\
${pkgs.jq}/bin/jq -c '.macaroon' | ${pkgs.xxd}/bin/xxd -p -r > "$macaroonPath"
chown ${cfg.macaroons.${macaroon}.user}: "$macaroonPath"
'') (attrNames cfg.macaroons)}
''}"
];
} // (if cfg.enforceTor
Expand All @@ -232,6 +270,7 @@ in {
lnd-wallet-password.user = "lnd";
lnd-key.user = "lnd";
lnd-cert.user = "lnd";
lnd-cert.permissions = "0444"; # world readable
};
};
}
1 change: 1 addition & 0 deletions modules/modules.nix
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
./netns-isolation.nix
./security.nix
./backups.nix
./btcpayserver.nix
];

disabledModules = [ "services/networking/bitcoind.nix" ];
Expand Down
15 changes: 15 additions & 0 deletions modules/netns-isolation.nix
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ in {
${ipNetns} route add default via ${bridgeIp}
${netnsIptables} -w -P INPUT DROP
${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
${netnsIptables} -w -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
'' + (optionalString (config.services.${n}.enforceTor or false)) ''
${netnsIptables} -w -P OUTPUT DROP
${netnsIptables} -w -A OUTPUT -d 127.0.0.1,${bridgeIp},${v.address} -j ACCEPT
Expand Down Expand Up @@ -230,6 +232,16 @@ in {
id = 22;
connections = [ "lnd" ];
};
nbxplorer = {
id = 23;
connections = [ "bitcoind" ];
};
btcpayserver = {
id = 24;
connections = [ "nbxplorer" ]
++ optional (config.services.btcpayserver.lightningBackend == "lnd") "lnd";
# communicates with clightning over rpc socket
};
};

services.bitcoind = {
Expand Down Expand Up @@ -299,6 +311,9 @@ in {
};

services.lightning-loop.cliExec = mkCliExec "lightning-loop";

services.nbxplorer.bind = netns.nbxplorer.address;
services.btcpayserver.bind = netns.btcpayserver.address;
}
]);
}
Loading

0 comments on commit a9c163c

Please sign in to comment.