Merge #133: Improve modularity, remove dependency on nixops, add modules test

187ff884db add modules test (Erik Arvstedt)
826245484e make secrets dir location configurable (Erik Arvstedt)
b1e13e9415 simplify secrets file format (Erik Arvstedt)
314272a228 lnd, nanopos: move user and group definitions to the bottom (Erik Arvstedt)
766fa4f300 travis: cache all build outputs with cachix (Erik Arvstedt)
b0e759160d travis: set NIX_PATH as early as possible (Erik Arvstedt)
c51bbcf104 travis: move comment (Erik Arvstedt)
7092dce0c7 travis: remove use of deprecated statements (Erik Arvstedt)
190a92507c travis: split up scripts into statements (Erik Arvstedt)
10d6b04ac8 support enabling clightning and lnd simultaneously (Erik Arvstedt)
ad7a519284 bitcoind: wait until RPC port is open (Erik Arvstedt)
5536b64fb3 lnd: wait until wallet is created (Erik Arvstedt)
6f2a55d63c lnd: wait until RPC port is open (Erik Arvstedt)
1868bef462 lnd: add option 'rpcPort' (Erik Arvstedt)
120e3e8cfe lnd postStart: suppress curl response output (Erik Arvstedt)
3e86637327 lnd postStart: poll for REST service availability (Erik Arvstedt)
795c51dc01 lnd postStart: make more idiomatic (Erik Arvstedt)
6e58beae8a lnd: use postStart option for script (Erik Arvstedt)
86167c6e6d clightning: wait until the RPC socket appears (Erik Arvstedt)
60c732a6a1 onion-chef: set RemainAfterExit, fix tor dependency (Erik Arvstedt)
2b9b3ba1c5 systemPackages: improve readability with shorter service references (Erik Arvstedt)
14ecb5511a liquid: add cli option (Erik Arvstedt)
cd5ed39b9c lnd: add cli option (Erik Arvstedt)
1833b15888 clightning: add cli option (Erik Arvstedt)
b90bf6691b add generate-secrets.service (Erik Arvstedt)
6447694214 add generate-secrets pkg (Erik Arvstedt)
e34093a8ac generate_secrets.sh: add opensslConf option (Erik Arvstedt)
9d14d5ba64 generate_secrets.sh: write secrets to working directory (Erik Arvstedt)
51fb054001 generate_secrets.sh: extract makepw command (Erik Arvstedt)
e3b47ce18a add setup-secrets.service (Erik Arvstedt)
437b268433 extract make-secrets.nix (Erik Arvstedt)
f9c29b9318 simplify secret definitions (Erik Arvstedt)
cd0fd6926b don't copy secret files to store during nixops deployment (Erik Arvstedt)
f0a36fe0c7 add 'nix-bitcoin-services' option (Erik Arvstedt)
7aaf30501c nix-bitcoin-services: simplify formatting (Erik Arvstedt)
760da232e0 add nix-bitcoin pkgs namespace (Erik Arvstedt)
6def181dbc add modules.nix (Erik Arvstedt)
3b842e5fe7 add nix-bitcoin-secrets.target (Erik Arvstedt)
bbf2bbc04a network.nix: simplify import of main config (Erik Arvstedt)
7e021a2629 simplify overlay.nix (Erik Arvstedt)
07dc3e04ac move bitcoinrpc group definition to bitcoind (Erik Arvstedt)
d61b185c3a simplify user and group definitions (Erik Arvstedt)

Pull request description:

  The nix-bitcoin modules consist of three fundamental components:
  1. a set of bitcoin-related modules for general use.
  2. an opinionated configuration of these modules (`nix-bitcoin.nix`), to be deployed on a
     dedicated machine.
  3. machinery for nixops deployment.

  This PR removes dependencies that reach from top to bottom in the list.
  This means that 1. is now usable on its own and that 2. can be used without 3.

  Besides improving nix-bitcoin's general usefulness, this
  - simplifies testing. This PR includes a Travis-enabled modules test using the NixOS testing framework.
  - paves the way for krops deployment.
  - unlocks direct deployment in NixOS containers which allows for super fast experimentation.

  ### Details
  Here are the unnecessary inter-component dependencies and how they're resolved by the commits. I'm using the numbering from the list above.

  - `1. -> 3.` The modules (1.) use the nixops-specific (3.) `keys` group.
    Resolved by `add nix-bitcoin-secrets.target`.

  - `1. -> 3.` 1. requires nixops-specific key services.
    Resolved by `add nix-bitcoin-secrets.target`.

  - `1. -> 2.` bitcoind needs the bitcoinrpc group which is defined in `nix-bitcoin.nix` (2.).
    Resolved by `move bitcoinrpc group definition to bitcoind`.

  Further obstacles for standalone usage of 1.:

  - We can't easily import 1. as a standalone module set.
    Resolved by `add modules.nix`.

  - Users of 1. shouldn't be forced to import nix-bitcoin's packages as top-level items in the pkgs namespace.
    Resolved by `add nix-bitcoin pkgs namespace`.

  ### Non-nixops deployments
  Commit `add setup-secrets.service` simplifies non-nixops deployment methods like containers, NixOS VMs or krops.

  Secrets can now deployed as follows:
  1. create local secrets.
  2. transfer secrets to machine.
  3. on the machine, `setup-secrets.service` creates extra secrets from `secrets.nix` and sets owner and
     permissions for all secrets.

  As krops integrates step 2. we now have all ingredients for automatic krops deployment.

  The service is complicated by the creation of secrets like `bitcoin-rpcpassword` that are composed of attrs from `secrets.nix` instead of being simply backed by a file like `lnd_key`. We could simplify this by creating all secret files locally.

  Running nix-bitcoin in NixOS containers gives you faster rebuild cycles when developing. [Here's](https://gist.github.com/5db4fa7dd3f1137920b58e39647116f6) an example.

  ### Test
  The last commits starting with `clightning: add cli option` are testing-related and mostly fix non-critical bugs that were exposed by the test.

  All `STABLE=1` builds from the Travis build matrix are implicit in the modules test.
  Should we remove these individual builds?

  Regarding commit `travis: cache all build outputs with cachix`:
  To replace my cache with a cache that's owned by you (maybe named `nix-bitcoin-ci`), run
  ```
  nix-shell -p travis --run 'travis encrypt CACHIX_SIGNING_KEY=... -r fort-nix/nix-bitcoin'
  ```
  where `...` is the value of `secretKey` in `~/.config/cachix/cachix.dhall`. Let me know the travis secret and I'll fixup the commit.

  ### Docs
  If you like the proposed changes, I'll add another PR with updates to the docs regarding the project layout, non-nixops deployment, and how to use nix-bitcoin within a larger NixOS config.

ACKs for top commit:
  jonasnick:
    ACK 187ff884db

Tree-SHA512: f4be65215c592a4f41bb7fa991a6d8d7c463cf631b88bf53051ca57ba280e7a60b8b09d0d1521345d5b656f844daa2166fff5d00a3105077c9e263465eacfb0a
This commit is contained in:
Jonas Nick
2020-01-13 08:22:06 +00:00
39 changed files with 990 additions and 396 deletions

View File

@@ -3,8 +3,8 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.bitcoind;
inherit (config) nix-bitcoin-services;
pidFile = "${cfg.dataDir}/bitcoind.pid";
configFile = pkgs.writeText "bitcoin.conf" ''
${optionalString cfg.testnet "testnet=1"}
@@ -19,7 +19,7 @@ let
listen=${if cfg.listen then "1" else "0"}
# RPC server options
${optionalString (cfg.rpc.port != null) "rpcport=${toString cfg.rpc.port}"}
rpcport=${toString cfg.rpc.port}
${concatMapStringsSep "\n"
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
(attrValues cfg.rpc.users)
@@ -69,7 +69,7 @@ in {
package = mkOption {
type = types.package;
default = pkgs.blockchains.bitcoind;
default = pkgs.nix-bitcoin.bitcoind;
defaultText = "pkgs.blockchains.bitcoind";
description = "The package providing bitcoin binaries.";
};
@@ -108,9 +108,9 @@ in {
rpc = {
port = mkOption {
type = types.nullOr types.ints.u16;
default = null;
description = "Override the default port on which to listen for JSON-RPC connections.";
type = types.ints.u16;
default = 8332;
description = "Port on which to listen for JSON-RPC connections.";
};
users = mkOption {
default = {};
@@ -225,8 +225,8 @@ in {
environment.systemPackages = [ cfg.package ];
systemd.services.bitcoind = {
description = "Bitcoin daemon";
requires = [ "bitcoin-rpcpassword-key.service" ];
after = [ "network.target" "bitcoin-rpcpassword-key.service" ];
requires = [ "nix-bitcoin-secrets.target" ];
after = [ "network.target" "nix-bitcoin-secrets.target" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
if ! test -e ${cfg.dataDir}; then
@@ -238,9 +238,15 @@ in {
cp '${cfg.configFileOption}' '${cfg.dataDir}/bitcoin.conf'
chmod o-rw '${cfg.dataDir}/bitcoin.conf'
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
echo "rpcpassword=$(cat /secrets/bitcoin-rpcpassword)" >> '${cfg.dataDir}/bitcoin.conf'
echo "rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/bitcoin.conf'
chmod -R g+rX '${cfg.dataDir}/blocks'
'';
# Wait until RPC port is open. This usually takes just a few ms.
postStart = ''
while ! { exec 3>/dev/tcp/127.0.0.1/${toString cfg.rpc.port}; } &>/dev/null; do
sleep 0.05
done
'';
serviceConfig = {
Type = "simple";
User = "${cfg.user}";
@@ -295,14 +301,16 @@ in {
};
users.users.${cfg.user} = {
name = cfg.user;
group = cfg.group;
extraGroups = [ "keys" ];
description = "Bitcoin daemon user";
home = cfg.dataDir;
};
users.groups.${cfg.group} = {
name = cfg.group;
users.groups.${cfg.group} = {};
users.groups.bitcoinrpc = {};
nix-bitcoin.secrets.bitcoin-rpcpassword = {
user = "bitcoin";
group = "bitcoinrpc";
};
};
}

View File

@@ -3,8 +3,8 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.clightning;
inherit (config) nix-bitcoin-services;
configFile = pkgs.writeText "config" ''
autolisten=${if cfg.autolisten then "true" else "false"}
network=bitcoin
@@ -57,6 +57,16 @@ in {
default = "/var/lib/clightning";
description = "The data directory for clightning.";
};
cli = mkOption {
readOnly = true;
default = pkgs.writeScriptBin "lightning-cli"
# Switch user because c-lightning doesn't allow setting the permissions of the rpc socket
# https://github.com/ElementsProject/lightning/issues/1366
''
exec sudo -u clightning ${pkgs.nix-bitcoin.clightning}/bin/lightning-cli --lightning-dir='${cfg.dataDir}' "$@"
'';
description = "Binary to connect with the clightning instance.";
};
enforceTor = nix-bitcoin-services.enforceTor;
};
@@ -64,16 +74,14 @@ in {
users.users.clightning = {
description = "clightning User";
group = "clightning";
extraGroups = [ "bitcoinrpc" "keys" ];
extraGroups = [ "bitcoinrpc" ];
home = cfg.dataDir;
};
users.groups.clightning = {
name = "clightning";
};
users.groups.clightning = {};
systemd.services.clightning = {
description = "Run clightningd";
path = [ pkgs.blockchains.bitcoind ];
path = [ pkgs.nix-bitcoin.bitcoind ];
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
@@ -85,11 +93,11 @@ in {
chmod u=rw,g=r,o= ${cfg.dataDir}/config
# The RPC socket has to be removed otherwise we might have stale sockets
rm -f ${cfg.dataDir}/lightning-rpc
echo "bitcoin-rpcpassword=$(cat /secrets/bitcoin-rpcpassword)" >> '${cfg.dataDir}/config'
echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/config'
'';
serviceConfig = {
PermissionsStartOnly = "true";
ExecStart = "${pkgs.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
ExecStart = "${pkgs.nix-bitcoin.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
User = "clightning";
Restart = "on-failure";
RestartSec = "10s";
@@ -98,6 +106,11 @@ in {
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
);
# Wait until the rpc socket appears
postStart = ''
while read f; do [[ $f == lightning-rpc ]] && break; done \
< <(${pkgs.inotifyTools}/bin/inotifywait --quiet --monitor -e create,moved_to --format '%f' '${cfg.dataDir}')
'';
};
};
}

View File

@@ -1,4 +1,5 @@
{
modules = ./modules.nix;
bitcoind = ./bitcoind.nix;
clightning = ./clightning.nix;
default = ./default.nix;
@@ -7,7 +8,6 @@
liquid = ./liquid.nix;
nanopos = ./nanopos.nix;
nix-bitcoin = ./nix-bitcoin.nix;
nix-bitcoin-pkgs = ./nix-bitcoin-pkgs.nix;
nix-bitcoin-webindex = ./nix-bitcoin-webindex.nix;
spark-wallet = ./spark-wallet.nix;
recurring-donations = ./recurring-donations.nix;

View File

@@ -3,8 +3,9 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.electrs;
inherit (config) nix-bitcoin-services;
secretsDir = config.nix-bitcoin.secretsDir;
index-batch-size = "${if cfg.high-memory then "" else "--index-batch-size=10"}";
jsonrpc-import = "${if cfg.high-memory then "" else "--jsonrpc-import"}";
in {
@@ -58,15 +59,12 @@ in {
config = mkIf cfg.enable {
users.users.${cfg.user} = {
name = cfg.user;
description = "electrs User";
group = cfg.group;
extraGroups = [ "bitcoinrpc" "keys" "bitcoin"];
extraGroups = [ "bitcoinrpc" "bitcoin"];
home = cfg.dataDir;
};
users.groups.electrs = {
name = cfg.group;
};
users.groups.${cfg.group} = {};
systemd.services.electrs = {
description = "Run electrs";
@@ -77,7 +75,7 @@ in {
preStart = ''
mkdir -m 0770 -p ${cfg.dataDir}
chown -R '${cfg.user}:${cfg.group}' ${cfg.dataDir}
echo "${pkgs.electrs}/bin/electrs -vvv ${index-batch-size} ${jsonrpc-import} --timestamp --db-dir ${cfg.dataDir} --daemon-dir /var/lib/bitcoind --cookie=${config.services.bitcoind.rpcuser}:$(cat /secrets/bitcoin-rpcpassword) --electrum-rpc-addr=127.0.0.1:${toString cfg.port}" > /run/electrs/startscript.sh
echo "${pkgs.nix-bitcoin.electrs}/bin/electrs -vvv ${index-batch-size} ${jsonrpc-import} --timestamp --db-dir ${cfg.dataDir} --daemon-dir /var/lib/bitcoind --cookie=${config.services.bitcoind.rpcuser}:$(cat ${secretsDir}/bitcoin-rpcpassword) --electrum-rpc-addr=127.0.0.1:${toString cfg.port}" > /run/electrs/startscript.sh
'';
serviceConfig = rec {
RuntimeDirectory = "electrs";
@@ -106,8 +104,8 @@ in {
listen ${toString config.services.electrs.nginxport} ssl;
proxy_pass electrs;
ssl_certificate /secrets/nginx_cert;
ssl_certificate_key /secrets/nginx_key;
ssl_certificate ${secretsDir}/nginx-cert;
ssl_certificate_key ${secretsDir}/nginx-key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 4h;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
@@ -116,5 +114,16 @@ in {
}
'';
};
systemd.services.nginx = {
requires = [ "nix-bitcoin-secrets.target" ];
after = [ "nix-bitcoin-secrets.target" ];
};
nix-bitcoin.secrets = rec {
nginx-key = {
user = "nginx";
group = "root";
};
nginx-cert = nginx-key;
};
};
}

View File

@@ -3,8 +3,8 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.lightning-charge;
inherit (config) nix-bitcoin-services;
in {
options.services.lightning-charge = {
enable = mkOption {
@@ -30,8 +30,8 @@ in {
requires = [ "clightning.service" ];
after = [ "clightning.service" ];
serviceConfig = {
EnvironmentFile = "/secrets/lightning-charge-api-token";
ExecStart = "${pkgs.lightning-charge}/bin/charged -l ${config.services.clightning.dataDir} -d ${config.services.clightning.dataDir}/lightning-charge.db";
EnvironmentFile = "${config.nix-bitcoin.secretsDir}/lightning-charge-env";
ExecStart = "${pkgs.nix-bitcoin.lightning-charge}/bin/charged -l ${config.services.clightning.dataDir} -d ${config.services.clightning.dataDir}/lightning-charge.db";
# Unfortunately c-lightning doesn't allow setting the permissions of the rpc socket,
# so this must run as the clightning user
# https://github.com/ElementsProject/lightning/issues/1366
@@ -42,5 +42,6 @@ in {
// nix-bitcoin-services.nodejs
// nix-bitcoin-services.allowTor;
};
nix-bitcoin.secrets.lightning-charge-env.user = "clightning";
};
}

View File

@@ -3,8 +3,9 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.liquidd;
inherit (config) nix-bitcoin-services;
secretsDir = config.nix-bitcoin.secretsDir;
pidFile = "${cfg.dataDir}/liquidd.pid";
configFile = pkgs.writeText "elements.conf" ''
chain=liquidv1
@@ -175,16 +176,30 @@ in {
Validate pegin claims. All functionaries must run this.
'';
};
cli = mkOption {
readOnly = true;
default = pkgs.writeScriptBin "elements-cli" ''
exec ${pkgs.nix-bitcoin.elementsd}/bin/elements-cli -datadir='${cfg.dataDir}' "$@"
'';
description = "Binary to connect with the liquidd instance.";
};
swap-cli = mkOption {
readOnly = true;
default = pkgs.writeScriptBin "liquidswap-cli" ''
exec ${pkgs.nix-bitcoin.liquid-swap}/bin/liquidswap-cli -c '${cfg.dataDir}/elements.conf' "$@"
'';
description = "Binary for managing liquid swaps.";
};
enforceTor = nix-bitcoin-services.enforceTor;
};
};
config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.elementsd ];
environment.systemPackages = [ pkgs.nix-bitcoin.elementsd ];
systemd.services.liquidd = {
description = "Elements daemon providing access to the Liquid sidechain";
requires = [ "liquid-rpcpassword-key.service" ];
after = [ "network.target" "liquid-rpcpassword-key.service" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
wantedBy = [ "multi-user.target" ];
preStart = ''
if ! test -e ${cfg.dataDir}; then
@@ -193,14 +208,14 @@ in {
cp '${configFile}' '${cfg.dataDir}/elements.conf'
chmod o-rw '${cfg.dataDir}/elements.conf'
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
echo "rpcpassword=$(cat /secrets/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf'
echo "mainchainrpcpassword=$(cat /secrets/bitcoin-rpcpassword)" >> '${cfg.dataDir}/elements.conf'
echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf'
echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/elements.conf'
'';
serviceConfig = {
Type = "simple";
User = "${cfg.user}";
Group = "${cfg.group}";
ExecStart = "${pkgs.elementsd}/bin/elementsd ${cmdlineOptions}";
ExecStart = "${pkgs.nix-bitcoin.elementsd}/bin/elementsd ${cmdlineOptions}";
StateDirectory = "liquidd";
PIDFile = "${pidFile}";
Restart = "on-failure";
@@ -214,14 +229,11 @@ in {
);
};
users.users.${cfg.user} = {
name = cfg.user;
group = cfg.group;
extraGroups = [ "keys" ];
description = "Liquid sidechain user";
home = cfg.dataDir;
};
users.groups.${cfg.group} = {
name = cfg.group;
};
users.groups.${cfg.group} = {};
nix-bitcoin.secrets.liquid-rpcpassword.user = "liquid";
};
}

View File

@@ -3,14 +3,17 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.lnd;
inherit (config) nix-bitcoin-services;
secretsDir = config.nix-bitcoin.secretsDir;
configFile = pkgs.writeText "lnd.conf" ''
datadir=${cfg.dataDir}
logdir=${cfg.dataDir}/logs
bitcoin.mainnet=1
tlscertpath=/secrets/lnd_cert
tlskeypath=/secrets/lnd_key
tlscertpath=${secretsDir}/lnd-cert
tlskeypath=${secretsDir}/lnd-key
rpclisten=localhost:${toString cfg.rpcPort}
bitcoin.active=1
bitcoin.node=bitcoind
@@ -26,45 +29,6 @@ let
${cfg.extraConfig}
'';
init-lnd-wallet-script = pkgs.writeScript "init-lnd-wallet.sh" ''
#!/bin/sh
set -e
umask 377
${pkgs.coreutils}/bin/sleep 5
if [ ! -f /secrets/lnd-seed-mnemonic ]
then
${pkgs.coreutils}/bin/echo Creating lnd seed
${pkgs.curl}/bin/curl -s \
--cacert /secrets/lnd_cert \
-X GET https://127.0.0.1:8080/v1/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > /secrets/lnd-seed-mnemonic
fi
if [ ! -f ${cfg.dataDir}/chain/bitcoin/mainnet/wallet.db ]
then
${pkgs.coreutils}/bin/echo Creating lnd wallet
${pkgs.curl}/bin/curl -s \
--cacert /secrets/lnd_cert \
-X POST -d "{\"wallet_password\": \"$(${pkgs.coreutils}/bin/cat /secrets/lnd-wallet-password | ${pkgs.coreutils}/bin/tr -d '\n' | ${pkgs.coreutils}/bin/base64 -w0)\", \
\"cipher_seed_mnemonic\": $(${pkgs.coreutils}/bin/cat /secrets/lnd-seed-mnemonic | ${pkgs.coreutils}/bin/tr -d '\n')}" \
https://127.0.0.1:8080/v1/initwallet
else
${pkgs.coreutils}/bin/echo Unlocking lnd wallet
${pkgs.curl}/bin/curl -s \
-H "Grpc-Metadata-macaroon: $(${pkgs.xxd}/bin/xxd -ps -u -c 99999 ${cfg.dataDir}/chain/bitcoin/mainnet/admin.macaroon)" \
--cacert /secrets/lnd_cert \
-X POST \
-d "{\"wallet_password\": \"$(${pkgs.coreutils}/bin/cat /secrets/lnd-wallet-password | ${pkgs.coreutils}/bin/tr -d '\n' | ${pkgs.coreutils}/bin/base64 -w0)\"}" \
https://127.0.0.1:8080/v1/unlockwallet
fi
exit 0
'';
in {
options.services.lnd = {
@@ -80,6 +44,11 @@ in {
default = "/var/lib/lnd";
description = "The data directory for LND.";
};
rpcPort = mkOption {
type = types.ints.u16;
default = 10009;
description = "Port on which to listen for gRPC connections.";
};
extraConfig = mkOption {
type = types.lines;
default = "";
@@ -88,23 +57,23 @@ in {
'';
description = "Additional configurations to be appended to <filename>lnd.conf</filename>.";
};
cli = mkOption {
readOnly = true;
default = pkgs.writeScriptBin "lncli"
# Switch user because lnd makes datadir contents readable by user only
''
exec sudo -u lnd ${pkgs.nix-bitcoin.lnd}/bin/lncli --tlscertpath ${secretsDir}/lnd-cert \
--macaroonpath '${cfg.dataDir}/chain/bitcoin/mainnet/admin.macaroon' "$@"
'';
description = "Binary to connect with the lnd instance.";
};
enforceTor = nix-bitcoin-services.enforceTor;
};
config = mkIf cfg.enable {
users.users.lnd = {
description = "LND User";
group = "lnd";
extraGroups = [ "bitcoinrpc" "keys" ];
home = cfg.dataDir;
};
users.groups.lnd = {
name = "lnd";
};
systemd.services.lnd = {
description = "Run LND";
path = [ pkgs.blockchains.bitcoind ];
path = [ pkgs.nix-bitcoin.bitcoind ];
wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ];
@@ -113,12 +82,11 @@ in {
cp ${configFile} ${cfg.dataDir}/lnd.conf
chown -R 'lnd:lnd' '${cfg.dataDir}'
chmod u=rw,g=r,o= ${cfg.dataDir}/lnd.conf
echo "bitcoind.rpcpass=$(cat /secrets/bitcoin-rpcpassword)" >> '${cfg.dataDir}/lnd.conf'
echo "bitcoind.rpcpass=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/lnd.conf'
'';
serviceConfig = {
PermissionsStartOnly = "true";
ExecStart = "${pkgs.lnd}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf";
ExecStartPost = "${pkgs.bash}/bin/bash ${init-lnd-wallet-script}";
ExecStart = "${pkgs.nix-bitcoin.lnd}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf";
User = "lnd";
Restart = "on-failure";
RestartSec = "10s";
@@ -127,6 +95,67 @@ in {
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
) // nix-bitcoin-services.allowAnyProtocol; # For ZMQ
postStart = let
mainnetDir = "${cfg.dataDir}/chain/bitcoin/mainnet";
in ''
umask 377
attempts=50
while ! { exec 3>/dev/tcp/127.0.0.1/8080 && exec 3>&-; } &>/dev/null; do
((attempts-- == 0)) && { echo "lnd REST service unreachable"; exit 1; }
sleep 0.1
done
if [[ ! -f ${secretsDir}/lnd-seed-mnemonic ]]; then
echo Create lnd seed
${pkgs.curl}/bin/curl -s \
--cacert ${secretsDir}/lnd-cert \
-X GET https://127.0.0.1:8080/v1/genseed | ${pkgs.jq}/bin/jq -c '.cipher_seed_mnemonic' > ${secretsDir}/lnd-seed-mnemonic
fi
if [[ ! -f ${mainnetDir}/wallet.db ]]; then
echo Create lnd wallet
${pkgs.curl}/bin/curl -s --output /dev/null --show-error \
--cacert ${secretsDir}/lnd-cert \
-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')}" \
https://127.0.0.1:8080/v1/initwallet
# Guarantees that RPC calls with cfg.cli succeed after the service is started
echo Wait until wallet is created
while [[ ! -f ${mainnetDir}/admin.macaroon ]]; do
sleep 0.1
done
else
echo Unlock lnd wallet
${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 "{\"wallet_password\": \"$(cat ${secretsDir}/lnd-wallet-password | tr -d '\n' | base64 -w0)\"}" \
https://127.0.0.1:8080/v1/unlockwallet
fi
# Wait until the RPC port is open
while ! { exec 3>/dev/tcp/127.0.0.1/${toString cfg.rpcPort}; } &>/dev/null; do
sleep 0.1
done
'';
};
users.users.lnd = {
description = "LND User";
group = "lnd";
extraGroups = [ "bitcoinrpc" ];
home = cfg.dataDir;
};
users.groups.lnd = {};
nix-bitcoin.secrets = {
lnd-wallet-password.user = "lnd";
lnd-key.user = "lnd";
lnd-cert.user = "lnd";
};
};
}

43
modules/modules.nix Normal file
View File

@@ -0,0 +1,43 @@
{ config, pkgs, lib, ... }:
let
nixpkgs-pinned = import ../pkgs/nixpkgs-pinned.nix;
unstable = import nixpkgs-pinned.nixpkgs-unstable {};
allPackages = pkgs: (import ../pkgs { inherit pkgs; }) // {
bitcoin = unstable.bitcoin.override { miniupnpc = null; };
bitcoind = unstable.bitcoind.override { miniupnpc = null; };
clightning = unstable.clightning;
lnd = unstable.lnd;
};
in {
imports = [
./bitcoind.nix
./clightning.nix
./lightning-charge.nix
./nanopos.nix
./nix-bitcoin-webindex.nix
./liquid.nix
./spark-wallet.nix
./electrs.nix
./onion-chef.nix
./recurring-donations.nix
./hardware-wallets.nix
./lnd.nix
./secrets/secrets.nix
];
disabledModules = [ "services/networking/bitcoind.nix" ];
options = {
nix-bitcoin-services = lib.mkOption {
readOnly = true;
default = import ./nix-bitcoin-services.nix lib;
};
};
config = {
nixpkgs.overlays = [ (self: super: {
nix-bitcoin = allPackages super;
}) ];
};
}

View File

@@ -3,8 +3,8 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.nanopos;
inherit (config) nix-bitcoin-services;
defaultItemsFile = pkgs.writeText "items.yaml" ''
tea:
price: 0.02 # denominated in the currency specified by --currency
@@ -52,24 +52,14 @@ in {
};
config = mkIf cfg.enable {
users.users.nanopos =
{
description = "nanopos User";
group = "nanopos";
extraGroups = [ "keys" ];
};
users.groups.nanopos = {
name = "nanopos";
};
systemd.services.nanopos = {
description = "Run nanopos";
wantedBy = [ "multi-user.target" ];
requires = [ "lightning-charge.service" ];
after = [ "lightning-charge.service" ];
serviceConfig = {
EnvironmentFile = "/secrets/lightning-charge-api-token-for-nanopos";
ExecStart = "${pkgs.nanopos}/bin/nanopos -y ${cfg.itemsFile} -p ${toString cfg.port} --show-bolt11";
EnvironmentFile = "${config.nix-bitcoin.secretsDir}/nanopos-env";
ExecStart = "${pkgs.nix-bitcoin.nanopos}/bin/nanopos -y ${cfg.itemsFile} -p ${toString cfg.port} --show-bolt11";
User = "nanopos";
Restart = "on-failure";
@@ -78,5 +68,11 @@ in {
// nix-bitcoin-services.nodejs
// nix-bitcoin-services.allowTor;
};
users.users.nanopos = {
description = "nanopos User";
group = "nanopos";
};
users.groups.nanopos = {};
nix-bitcoin.secrets.nanopos-env.user = "nanopos";
};
}

View File

@@ -1,17 +0,0 @@
{ config, pkgs, ... }:
let
nixpkgs-pinned = import ../pkgs/nixpkgs-pinned.nix;
nixpkgs-unstable = import nixpkgs-pinned.nixpkgs-unstable { };
in {
disabledModules = [ "services/networking/bitcoind.nix" ];
nixpkgs.overlays = [ (import ../overlay.nix) ];
nixpkgs.config.packageOverrides = pkgs: {
# Use bitcoin and clightning from unstable
bitcoin = nixpkgs-unstable.bitcoin.override { miniupnpc = null; };
blockchains.bitcoind = nixpkgs-unstable.bitcoind.override { miniupnpc = null; };
clightning = nixpkgs-unstable.clightning.override { };
lnd = nixpkgs-unstable.lnd.override { };
};
}

View File

@@ -1,11 +1,10 @@
# See `man systemd.exec` and `man systemd.resource-control` for an explanation
# of the various systemd options available through this module.
{ config, lib, pkgs, ... }:
lib:
with lib;
let
{
defaultHardening = {
PrivateTmp = "true";
ProtectSystem = "full";
@@ -23,9 +22,7 @@ let
SystemCallFilter= "accept accept4 access adjtimex alarm bind brk capget capset chdir chmod chown chown32 clock_getres clock_gettime clock_nanosleep close connect copy_file_range creat dup dup2 dup3 epoll_create epoll_create1 epoll_ctl epoll_ctl_old epoll_pwait epoll_wait epoll_wait_old eventfd eventfd2 execve execveat exit exit_group faccessat fadvise64 fadvise64_64 fallocate fanotify_mark fchdir fchmod fchmodat fchown fchown32 fchownat fcntl fcntl64 fdatasync fgetxattr flistxattr flock fork fremovexattr fsetxattr fstat fstat64 fstatat64 fstatfs fstatfs64 fsync ftruncate ftruncate64 futex futimesat getcpu getcwd getdents getdents64 getegid getegid32 geteuid geteuid32 getgid getgid32 getgroups getgroups32 getitimer getpeername getpgid getpgrp getpid getppid getpriority getrandom getresgid getresgid32 getresuid getresuid32 getrlimit get_robust_list getrusage getsid getsockname getsockopt get_thread_area gettid gettimeofday getuid getuid32 getxattr inotify_add_watch inotify_init inotify_init1 inotify_rm_watch io_cancel ioctl io_destroy io_getevents io_pgetevents ioprio_get ioprio_set io_setup io_submit ipc kill lchown lchown32 lgetxattr link linkat listen listxattr llistxattr _llseek lremovexattr lseek lsetxattr lstat lstat64 madvise memfd_create mincore mkdir mkdirat mknod mknodat mlock mlock2 mlockall mmap mmap2 mprotect mq_getsetattr mq_notify mq_open mq_timedreceive mq_timedsend mq_unlink mremap msgctl msgget msgrcv msgsnd msync munlock munlockall munmap nanosleep newfstatat _newselect open openat pause pipe pipe2 poll ppoll prctl pread64 preadv preadv2 prlimit64 pselect6 pwrite64 pwritev pwritev2 read readahead readlink readlinkat readv recv recvfrom recvmmsg recvmsg remap_file_pages removexattr rename renameat renameat2 restart_syscall rmdir rt_sigaction rt_sigpending rt_sigprocmask rt_sigqueueinfo rt_sigreturn rt_sigsuspend rt_sigtimedwait rt_tgsigqueueinfo sched_getaffinity sched_getattr sched_getparam sched_get_priority_max sched_get_priority_min sched_getscheduler sched_rr_get_interval sched_setaffinity sched_setattr sched_setparam sched_setscheduler sched_yield seccomp select semctl semget semop semtimedop send sendfile sendfile64 sendmmsg sendmsg sendto setfsgid setfsgid32 setfsuid setfsuid32 setgid setgid32 setgroups setgroups32 setitimer setpgid setpriority setregid setregid32 setresgid setresgid32 setresuid setresuid32 setreuid setreuid32 setrlimit set_robust_list setsid setsockopt set_thread_area set_tid_address setuid setuid32 setxattr shmat shmctl shmdt shmget shutdown sigaltstack signalfd signalfd4 sigreturn socket socketcall socketpair splice stat stat64 statfs statfs64 statx symlink symlinkat sync sync_file_range syncfs sysinfo tee tgkill time timer_create timer_delete timerfd_create timerfd_gettime timerfd_settime timer_getoverrun timer_gettime timer_settime times tkill truncate truncate64 ugetrlimit umask uname unlink unlinkat utime utimensat utimes vfork vmsplice wait4 waitid waitpid write writev arm_fadvise64_64 arm_sync_file_range sync_file_range2 breakpoint cacheflush set_tls arch_prctl modify_ldt clone";
SystemCallArchitectures= "native";
};
in
{
inherit defaultHardening;
# nodejs applications apparently rely on memory write execute
nodejs = { MemoryDenyWriteExecute = "false"; };
# Allow tor traffic. Allow takes precedence over Deny.

View File

@@ -3,8 +3,8 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.nix-bitcoin-webindex;
inherit (config) nix-bitcoin-services;
indexFile = pkgs.writeText "index.html" ''
<html>
<body>
@@ -74,7 +74,13 @@ in {
description = "Get node info";
wantedBy = [ "multi-user.target" ];
after = [ "nodeinfo.service" ];
path = [ pkgs.nodeinfo pkgs.clightning pkgs.jq pkgs.sudo ];
path = with pkgs; [
nix-bitcoin.nodeinfo
config.services.clightning.cli
config.services.lnd.cli
jq
sudo
];
serviceConfig = {
ExecStart="${pkgs.bash}/bin/bash ${createWebIndex}";
User = "root";

View File

@@ -15,21 +15,7 @@ let
chown -R operator ${config.users.users.operator.home}/.ssh
'';
in {
imports = [
./nix-bitcoin-pkgs.nix
./bitcoind.nix
./clightning.nix
./lightning-charge.nix
./nanopos.nix
./nix-bitcoin-webindex.nix
./liquid.nix
./spark-wallet.nix
./electrs.nix
./onion-chef.nix
./recurring-donations.nix
./hardware-wallets.nix
./lnd.nix
];
imports = [ ./modules.nix ];
options.services.nix-bitcoin = {
enable = mkOption {
@@ -42,6 +28,8 @@ in {
};
config = mkIf cfg.enable {
nix-bitcoin.secretsDir = mkDefault "/secrets";
networking.firewall.enable = true;
# Tor
@@ -85,9 +73,6 @@ in {
version = 3;
};
# Add bitcoinrpc group
users.groups.bitcoinrpc = {};
# clightning
services.clightning.bitcoin-rpcuser = config.services.bitcoind.rpcuser;
services.clightning.proxy = config.services.tor.client.socksListenAddress;
@@ -118,29 +103,15 @@ in {
services.onion-chef.enable = true;
services.onion-chef.access.operator = [ "bitcoind" "clightning" "nginx" "liquidd" "spark-wallet" "electrs" "sshd" ];
environment.interactiveShellInit = ''
${optionalString (config.services.clightning.enable) ''
alias lightning-cli='sudo -u clightning lightning-cli --lightning-dir=${config.services.clightning.dataDir}'
''}
${optionalString (config.services.lnd.enable) ''
alias lncli='sudo -u lnd lncli --tlscertpath /secrets/lnd_cert --macaroonpath ${config.services.lnd.dataDir}/chain/bitcoin/mainnet/admin.macaroon'
''}
${optionalString (config.services.liquidd.enable) ''
alias elements-cli='elements-cli -datadir=${config.services.liquidd.dataDir}'
alias liquidswap-cli='liquidswap-cli -c ${config.services.liquidd.dataDir}/elements.conf'
''}
'';
# Unfortunately c-lightning doesn't allow setting the permissions of the rpc socket
# https://github.com/ElementsProject/lightning/issues/1366
security.sudo.configFile = (
if config.services.clightning.enable then ''
operator ALL=(clightning) NOPASSWD: ALL
''
else if config.services.lnd.enable then ''
operator ALL=(lnd) NOPASSWD: ALL
''
else ""
);
security.sudo.configFile =
(optionalString config.services.clightning.enable ''
operator ALL=(clightning) NOPASSWD: ALL
'') +
(optionalString config.services.lnd.enable ''
operator ALL=(lnd) NOPASSWD: ALL
'');
# Give root ssh access to the operator account
systemd.services.copy-root-authorized-keys = {
@@ -184,30 +155,32 @@ in {
}];
version = 3;
};
environment.systemPackages = with pkgs; [
environment.systemPackages = with pkgs; with nix-bitcoin; let
s = config.services;
in
[
tor
blockchains.bitcoind
(hiPrio config.services.bitcoind.cli)
bitcoind
(hiPrio s.bitcoind.cli)
nodeinfo
jq
qrencode
]
++ optionals config.services.clightning.enable [clightning]
++ optionals config.services.lnd.enable [lnd]
++ optionals config.services.lightning-charge.enable [lightning-charge]
++ optionals config.services.nanopos.enable [nanopos]
++ optionals config.services.nix-bitcoin-webindex.enable [nginx]
++ optionals config.services.liquidd.enable [elementsd liquid-swap]
++ optionals config.services.spark-wallet.enable [spark-wallet]
++ optionals config.services.electrs.enable [electrs]
++ optionals (config.services.hardware-wallets.ledger || config.services.hardware-wallets.trezor) [
++ optionals s.clightning.enable [clightning (hiPrio s.clightning.cli)]
++ optionals s.lnd.enable [lnd (hiPrio s.lnd.cli)]
++ optionals s.lightning-charge.enable [lightning-charge]
++ optionals s.nanopos.enable [nanopos]
++ optionals s.nix-bitcoin-webindex.enable [nginx]
++ optionals s.liquidd.enable [elementsd (hiPrio s.liquidd.cli) (hiPrio s.liquidd.swap-cli)]
++ optionals s.spark-wallet.enable [spark-wallet]
++ optionals s.electrs.enable [electrs]
++ optionals (s.hardware-wallets.ledger || s.hardware-wallets.trezor) [
hwi
# To allow debugging issues with lsusb:
# To allow debugging issues with lsusb
usbutils
]
++ optionals config.services.hardware-wallets.trezor [
++ optionals s.hardware-wallets.trezor [
python3.pkgs.trezor
];
};
}

View File

@@ -8,8 +8,8 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.onion-chef;
inherit (config) nix-bitcoin-services;
dataDir = "/var/lib/onion-chef/";
onion-chef-script = pkgs.writeScript "onion-chef.sh" ''
# wait until tor is up
@@ -70,14 +70,13 @@ in {
config = mkIf cfg.enable {
systemd.services.onion-chef = {
description = "Run onion-chef";
wantedBy = [ "multi-user.target" ];
requires = [ "tor.service" ];
partOf = [ "tor.service" ];
wantedBy = [ "tor.service" ];
bindsTo = [ "tor.service" ];
after = [ "tor.service" ];
serviceConfig = {
ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}";
User = "root";
Type = "oneshot";
RemainAfterExit = true;
} // nix-bitcoin-services.defaultHardening;
};
};

View File

@@ -3,10 +3,10 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.recurring-donations;
inherit (config) nix-bitcoin-services;
recurring-donations-script = pkgs.writeScript "recurring-donations.sh" ''
LNCLI="lightning-cli --lightning-dir=${config.services.clightning.dataDir}"
LNCLI="${pkgs.nix-bitcoin.clightning}/bin/lightning-cli --lightning-dir=${config.services.clightning.dataDir}"
pay_tallycoin() {
NAME=$1
AMOUNT=$2
@@ -82,7 +82,7 @@ in {
description = "Run recurring-donations";
requires = [ "clightning.service" ];
after = [ "clightning.service" ];
path = [ pkgs.clightning pkgs.curl pkgs.torsocks pkgs.sudo pkgs.jq ];
path = with pkgs; [ nix-bitcoin.clightning curl torsocks sudo jq ];
serviceConfig = {
ExecStart = "${pkgs.bash}/bin/bash ${recurring-donations-script}";
# TODO: would be better if this was operator, but I don't get sudo

View File

@@ -0,0 +1,26 @@
{ config, pkgs, lib, ... }:
# This is mainly for testing.
# When using this for regular deployments, make sure to create a backup of the
# generated secrets.
with lib;
{
nix-bitcoin.setup-secrets = true;
systemd.services.generate-secrets = {
requiredBy = [ "setup-secrets.service" ];
before = [ "setup-secrets.service" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
} // config.nix-bitcoin-services.defaultHardening;
script = ''
mkdir -p "${config.nix-bitcoin.secretsDir}"
cd "${config.nix-bitcoin.secretsDir}"
chown root: .
chmod 0700 .
${pkgs.nix-bitcoin.generate-secrets}
'';
};
}

View File

@@ -0,0 +1,97 @@
{ config, pkgs, lib, ... }:
with lib;
let
cfg = config.nix-bitcoin;
setupSecrets = concatStrings (mapAttrsToList (n: v: ''
setupSecret ${n} ${v.user} ${v.group} ${v.permissions} }
'') cfg.secrets);
in
{
options.nix-bitcoin = {
secretsDir = mkOption {
type = types.path;
default = "/etc/nix-bitcoin-secrets";
description = "Directory to store secrets";
};
secrets = mkOption {
default = {};
type = with types; attrsOf (submodule (
{ config, ... }: {
options = {
user = mkOption {
type = str;
default = "root";
};
group = mkOption {
type = str;
default = config.user;
};
permissions = mkOption {
type = str;
default = "0440";
};
};
}
));
};
setup-secrets = mkEnableOption "Set permissions for secrets generated by 'generate-secrets.sh'";
};
config = mkIf cfg.setup-secrets {
systemd.targets.nix-bitcoin-secrets = {
requires = [ "setup-secrets.service" ];
after = [ "setup-secrets.service" ];
};
# Operation of this service:
# - Create missing secrets that are composed of attrs from secrets.nix
# - Set owner and permissions for all used secrets
# - Make all other secrets accessible to root only
# For all steps make sure that no secrets are copied to the nix store.
#
systemd.services.setup-secrets = {
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
} // config.nix-bitcoin-services.defaultHardening;
script = ''
setupSecret() {
file="$1"
user="$2"
group="$3"
permissions="$4"
if [[ ! -e $file ]]; then
echo "Error: Secret file '$file' is missing"
exit 1
fi
chown "$user:$group" "$file"
chmod "$permissions" "$file"
processedFiles+=("$file")
}
dir="${cfg.secretsDir}"
if [[ ! -e $dir ]]; then
echo "Error: Secrets dir '$dir' is missing"
exit 1
fi
chown root: "$dir"
cd "$dir"
processedFiles=()
${setupSecrets}
# Make all other files accessible to root only
unprocessedFiles=$(comm -23 <(printf '%s\n' *) <(printf '%s\n' "''${processedFiles[@]}" | sort))
IFS=$'\n'
chown root: $unprocessedFiles
chmod 0440 $unprocessedFiles
# Now make the secrets dir accessible to other users
chmod 0751 "$dir"
'';
};
};
}

View File

@@ -3,12 +3,12 @@
with lib;
let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.spark-wallet;
inherit (config) nix-bitcoin-services;
dataDir = "/var/lib/spark-wallet/";
onion-chef-service = (if cfg.onion-service then [ "onion-chef.service" ] else []);
run-spark-wallet = pkgs.writeScript "run-spark-wallet" ''
CMD="${pkgs.spark-wallet}/bin/spark-wallet --ln-path ${cfg.ln-path} -Q -k -c /secrets/spark-wallet-login"
CMD="${pkgs.nix-bitcoin.spark-wallet}/bin/spark-wallet --ln-path ${cfg.ln-path} -Q -k -c ${config.nix-bitcoin.secretsDir}/spark-wallet-login"
${optionalString cfg.onion-service
''
echo Getting onion hostname
@@ -73,5 +73,6 @@ in {
// nix-bitcoin-services.nodejs
// nix-bitcoin-services.allowTor;
};
nix-bitcoin.secrets.spark-wallet-login.user = "clightning";
};
}