Merge #174: Hardening systemd

ccc3a70344 service hardening: add more restrictions (nixbitcoin)
3fbfa98635 service hardening: replace obtuse SystemCallFilter with @system-service (nixbitcoin)
e34d1c884e service hardening: Add PrivateUsers (nixbitcoin)
1c75543f2f clightning: add user and group options (nixbitcoin)
5f3f362451 lnd: add strict hardening (Erik Arvstedt)
a040e52854 All modules: ProtectSystem = strict (nixbitcoin)
adc71b892e Remove PermissionStartOnly where possible and replace with bitcoinrpc (nixbitcoin)
91b6b2c370 All modules with preStart: Use systemd.tmpfiles.rules (nixbitcoin)
423ebf862b lnd: only enable bitcoind zmqpub if lnd.enable (nixbitcoin)
81a1c3f908 service hardening: Add CapabilityBoundingSets (nixbitcoin)
3cd61506e0 webindex & onion-chef: Run non-network-facing services in PrivateNetwork (nixbitcoin)
7c70dd43ac All modules: Give service config precedence over defaultHardening (nixbitcoin)

Pull request description:

ACKs for top commit:
  erikarvstedt:
    ACK ccc3a70344
  jonasnick:
    ACK ccc3a70344 very nice

Tree-SHA512: 069f74b11b46b17fd180e9da5328a3b9952aa90100b5077251d1e56a4d64f03ba64587adf153ddc6cf42f750c13a168f9f0fe43bc379bcd4a9f6709e635e512a
This commit is contained in:
Jonas Nick 2020-05-26 11:17:42 +00:00
commit 8cc0b30902
No known key found for this signature in database
GPG Key ID: 4861DBF262123605
14 changed files with 173 additions and 129 deletions

View File

@ -255,19 +255,17 @@ in {
sysperms = true; sysperms = true;
}; };
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
"d '${cfg.dataDir}/blocks' 0770 ${cfg.user} ${cfg.group} - -"
];
systemd.services.bitcoind = { systemd.services.bitcoind = {
description = "Bitcoin daemon"; description = "Bitcoin daemon";
requires = [ "nix-bitcoin-secrets.target" ]; requires = [ "nix-bitcoin-secrets.target" ];
after = [ "network.target" "nix-bitcoin-secrets.target" ]; after = [ "network.target" "nix-bitcoin-secrets.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
preStart = '' preStart = ''
if [[ ! -e ${cfg.dataDir} ]]; then
mkdir -m 0770 -p '${cfg.dataDir}'
fi
if [[ ! -e ${cfg.dataDir}/blocks ]]; then
mkdir -m 0770 -p '${cfg.dataDir}/blocks'
fi
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
${optionalString cfg.dataDirReadableByGroup "chmod -R g+rX '${cfg.dataDir}/blocks'"} ${optionalString cfg.dataDirReadableByGroup "chmod -R g+rX '${cfg.dataDir}/blocks'"}
cfg=$(cat ${configFile}; printf "rpcpassword="; cat "${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword") cfg=$(cat ${configFile}; printf "rpcpassword="; cat "${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword")
@ -282,17 +280,14 @@ in {
sleep 0.05 sleep 0.05
done done
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.group}"; Group = "${cfg.group}";
ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'"; ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'";
Restart = "on-failure"; Restart = "on-failure";
UMask = mkIf cfg.dataDirReadableByGroup "0027"; UMask = mkIf cfg.dataDirReadableByGroup "0027";
ReadWritePaths = "${cfg.dataDir}";
# Permission for preStart } // (if cfg.enforceTor
PermissionsStartOnly = "true";
} // nix-bitcoin-services.defaultHardening
// (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP) else nix-bitcoin-services.allowAnyIP)
// optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nix-bitcoin-services.allowAnyProtocol; // optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nix-bitcoin-services.allowAnyProtocol;
@ -320,11 +315,11 @@ in {
fi fi
done done
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.group}"; Group = "${cfg.group}";
} // nix-bitcoin-services.defaultHardening ReadWritePaths = "${cfg.dataDir}";
// nix-bitcoin-services.allowTor; } // nix-bitcoin-services.allowTor;
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {
@ -332,9 +327,11 @@ in {
description = "Bitcoin daemon user"; description = "Bitcoin daemon user";
}; };
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = {};
users.groups.bitcoinrpc = {};
nix-bitcoin.secrets.bitcoin-rpcpassword = { nix-bitcoin.secrets.bitcoin-rpcpassword = {
user = "bitcoin"; user = "bitcoin";
group = "bitcoinrpc";
}; };
}; };
} }

View File

@ -59,6 +59,16 @@ in {
default = "/var/lib/clightning"; default = "/var/lib/clightning";
description = "The data directory for clightning."; description = "The data directory for clightning.";
}; };
user = mkOption {
type = types.str;
default = "clightning";
description = "The user as which to run clightning.";
};
group = mkOption {
type = types.str;
default = cfg.user;
description = "The group as which to run clightning.";
};
cli = mkOption { cli = mkOption {
readOnly = true; readOnly = true;
default = pkgs.writeScriptBin "lightning-cli" default = pkgs.writeScriptBin "lightning-cli"
@ -72,11 +82,16 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ pkgs.nix-bitcoin.clightning (hiPrio cfg.cli) ]; environment.systemPackages = [ pkgs.nix-bitcoin.clightning (hiPrio cfg.cli) ];
users.users.clightning = { users.users.${cfg.user} = {
description = "clightning User"; description = "clightning User";
group = "clightning"; group = cfg.group;
extraGroups = [ "bitcoinrpc" ];
}; };
users.groups.clightning = {}; users.groups.${cfg.group} = {};
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
systemd.services.clightning = { systemd.services.clightning = {
description = "Run clightningd"; description = "Run clightningd";
@ -85,22 +100,20 @@ in {
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
preStart = '' preStart = ''
mkdir -m 0770 -p ${cfg.dataDir}
cp ${configFile} ${cfg.dataDir}/config cp ${configFile} ${cfg.dataDir}/config
chown -R 'clightning:clightning' '${cfg.dataDir}' chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
# The RPC socket has to be removed otherwise we might have stale sockets # The RPC socket has to be removed otherwise we might have stale sockets
rm -f ${cfg.dataDir}/bitcoin/lightning-rpc rm -f ${cfg.dataDir}/bitcoin/lightning-rpc
chmod 600 ${cfg.dataDir}/config chmod 600 ${cfg.dataDir}/config
echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/config' echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/config'
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
PermissionsStartOnly = "true";
ExecStart = "${pkgs.nix-bitcoin.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; ExecStart = "${pkgs.nix-bitcoin.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}";
User = "clightning"; User = "${cfg.user}";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening ReadWritePaths = "${cfg.dataDir}";
// (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP else nix-bitcoin-services.allowAnyIP
); );

View File

@ -63,22 +63,23 @@ in {
config = mkIf cfg.enable (mkMerge [{ config = mkIf cfg.enable (mkMerge [{
environment.systemPackages = [ pkgs.nix-bitcoin.electrs ]; environment.systemPackages = [ pkgs.nix-bitcoin.electrs ];
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
systemd.services.electrs = { systemd.services.electrs = {
description = "Electrs Electrum Server"; description = "Electrs Electrum Server";
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
preStart = '' preStart = ''
mkdir -m 0770 -p ${cfg.dataDir}
chown -R '${cfg.user}:${cfg.group}' ${cfg.dataDir}
echo "cookie = \"${config.services.bitcoind.rpcuser}:$(cat ${secretsDir}/bitcoin-rpcpassword)\"" \ echo "cookie = \"${config.services.bitcoind.rpcuser}:$(cat ${secretsDir}/bitcoin-rpcpassword)\"" \
> electrs.toml > electrs.toml
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
RuntimeDirectory = "electrs"; RuntimeDirectory = "electrs";
RuntimeDirectoryMode = "700"; RuntimeDirectoryMode = "700";
WorkingDirectory = "/run/electrs"; WorkingDirectory = "/run/electrs";
PermissionsStartOnly = "true";
ExecStart = '' ExecStart = ''
${pkgs.nix-bitcoin.electrs}/bin/electrs -vvv \ ${pkgs.nix-bitcoin.electrs}/bin/electrs -vvv \
${if cfg.high-memory then ${if cfg.high-memory then
@ -96,8 +97,8 @@ in {
Group = cfg.group; Group = cfg.group;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening ReadWritePaths = "${cfg.dataDir} ${if cfg.high-memory then "${config.services.bitcoind.dataDir}" else ""}";
// (if cfg.enforceTor } // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP else nix-bitcoin-services.allowAnyIP
); );
@ -106,7 +107,7 @@ in {
users.users.${cfg.user} = { users.users.${cfg.user} = {
description = "electrs User"; description = "electrs User";
group = cfg.group; group = cfg.group;
extraGroups = optionals cfg.high-memory [ "bitcoin" ]; extraGroups = [ "bitcoinrpc" ] ++ optionals cfg.high-memory [ "bitcoin" ];
}; };
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = {};
} }

View File

@ -50,15 +50,16 @@ in {
chmod 600 ${cfg.dataDir}/lightning-charge.db chmod 600 ${cfg.dataDir}/lightning-charge.db
fi fi
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
# Needed to access clightning.dataDir in preStart
PermissionsStartOnly = "true"; PermissionsStartOnly = "true";
EnvironmentFile = "${config.nix-bitcoin.secretsDir}/lightning-charge-env"; EnvironmentFile = "${config.nix-bitcoin.secretsDir}/lightning-charge-env";
ExecStart = "${pkgs.nix-bitcoin.lightning-charge}/bin/charged -l ${config.services.clightning.dataDir}/bitcoin -d ${cfg.dataDir}/lightning-charge.db"; ExecStart = "${pkgs.nix-bitcoin.lightning-charge}/bin/charged -l ${config.services.clightning.dataDir}/bitcoin -d ${cfg.dataDir}/lightning-charge.db";
User = user; User = user;
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening ReadWritePaths = "${cfg.dataDir}";
// nix-bitcoin-services.nodejs } // nix-bitcoin-services.nodejs
// nix-bitcoin-services.allowTor; // nix-bitcoin-services.allowTor;
}; };
nix-bitcoin.secrets.lightning-charge-env.user = user; nix-bitcoin.secrets.lightning-charge-env.user = user;

View File

@ -200,40 +200,39 @@ in {
(hiPrio cfg.cli) (hiPrio cfg.cli)
(hiPrio cfg.swap-cli) (hiPrio cfg.swap-cli)
]; ];
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -"
];
systemd.services.liquidd = { systemd.services.liquidd = {
description = "Elements daemon providing access to the Liquid sidechain"; description = "Elements daemon providing access to the Liquid sidechain";
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
preStart = '' preStart = ''
if ! test -e ${cfg.dataDir}; then
mkdir -m 0770 -p '${cfg.dataDir}'
fi
cp '${configFile}' '${cfg.dataDir}/elements.conf' cp '${configFile}' '${cfg.dataDir}/elements.conf'
chmod o-rw '${cfg.dataDir}/elements.conf' chmod 640 '${cfg.dataDir}/elements.conf'
chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}' chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}'
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)" >> '${cfg.dataDir}/elements.conf' echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/elements.conf'
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
Type = "simple"; Type = "simple";
User = "${cfg.user}"; User = "${cfg.user}";
Group = "${cfg.group}"; Group = "${cfg.group}";
ExecStart = "${pkgs.nix-bitcoin.elementsd}/bin/elementsd ${cmdlineOptions}"; ExecStart = "${pkgs.nix-bitcoin.elementsd}/bin/elementsd ${cmdlineOptions}";
StateDirectory = "liquidd";
PIDFile = "${pidFile}"; PIDFile = "${pidFile}";
Restart = "on-failure"; Restart = "on-failure";
ReadWritePaths = "${cfg.dataDir}";
# Permission for preStart } // (if cfg.enforceTor
PermissionsStartOnly = "true";
} // nix-bitcoin-services.defaultHardening
// (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP else nix-bitcoin-services.allowAnyIP
); );
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {
group = cfg.group; group = cfg.group;
extraGroups = [ "bitcoinrpc" ];
description = "Liquid sidechain user"; description = "Liquid sidechain user";
}; };
users.groups.${cfg.group} = {}; users.groups.${cfg.group} = {};

View File

@ -78,6 +78,16 @@ in {
config = mkIf cfg.enable { config = mkIf cfg.enable {
environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ]; environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ];
systemd.tmpfiles.rules = [
"d '${cfg.dataDir}' 0770 lnd lnd - -"
];
services.bitcoind = {
zmqpubrawblock = "tcp://127.0.0.1:28332";
zmqpubrawtx = "tcp://127.0.0.1:28333";
};
systemd.services.lnd = { systemd.services.lnd = {
description = "Run LND"; description = "Run LND";
path = [ pkgs.nix-bitcoin.bitcoind ]; path = [ pkgs.nix-bitcoin.bitcoind ];
@ -85,76 +95,78 @@ in {
requires = [ "bitcoind.service" ]; requires = [ "bitcoind.service" ];
after = [ "bitcoind.service" ]; after = [ "bitcoind.service" ];
preStart = '' preStart = ''
mkdir -m 0770 -p ${cfg.dataDir} install -m600 ${configFile} '${cfg.dataDir}/lnd.conf'
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 ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/lnd.conf' echo "bitcoind.rpcpass=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/lnd.conf'
''; '';
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
PermissionsStartOnly = "true";
ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf"; ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf";
User = "lnd"; User = "lnd";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening ReadWritePaths = "${cfg.dataDir}";
// (if cfg.enforceTor ExecStartPost = [
# Run fully privileged for secrets dir write access
"+${nix-bitcoin-services.script ''
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
mnemonic=${secretsDir}/lnd-seed-mnemonic
if [[ ! -f $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' > "$mnemonic"
fi
chown lnd: "$mnemonic"
chmod 400 "$mnemonic"
''}"
"${let
mainnetDir = "${cfg.dataDir}/chain/bitcoin/mainnet";
in nix-bitcoin-services.script ''
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
''}"
];
} // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP else nix-bitcoin-services.allowAnyIP
) // nix-bitcoin-services.allowAnyProtocol; # For ZMQ ) // 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 = { users.users.lnd = {
description = "LND User"; description = "LND User";
group = "lnd"; group = "lnd";
extraGroups = [ "bitcoinrpc" ];
home = cfg.dataDir; # lnd creates .lnd dir in HOME home = cfg.dataDir; # lnd creates .lnd dir in HOME
}; };
users.groups.lnd = {}; users.groups.lnd = {};

View File

@ -21,7 +21,7 @@
options = { options = {
nix-bitcoin-services = lib.mkOption { nix-bitcoin-services = lib.mkOption {
readOnly = true; readOnly = true;
default = import ./nix-bitcoin-services.nix lib; default = import ./nix-bitcoin-services.nix lib pkgs;
}; };
}; };

View File

@ -58,14 +58,13 @@ in {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "lightning-charge.service" ]; requires = [ "lightning-charge.service" ];
after = [ "lightning-charge.service" ]; after = [ "lightning-charge.service" ];
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
EnvironmentFile = "${config.nix-bitcoin.secretsDir}/nanopos-env"; EnvironmentFile = "${config.nix-bitcoin.secretsDir}/nanopos-env";
ExecStart = "${pkgs.nix-bitcoin.nanopos}/bin/nanopos -y ${cfg.itemsFile} -p ${toString cfg.port} --show-bolt11"; ExecStart = "${pkgs.nix-bitcoin.nanopos}/bin/nanopos -y ${cfg.itemsFile} -p ${toString cfg.port} --show-bolt11";
User = "nanopos"; User = "nanopos";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening } // nix-bitcoin-services.nodejs
// nix-bitcoin-services.nodejs
// nix-bitcoin-services.allowTor; // nix-bitcoin-services.allowTor;
}; };
users.users.nanopos = { users.users.nanopos = {

View File

@ -1,13 +1,14 @@
# See `man systemd.exec` and `man systemd.resource-control` for an explanation # See `man systemd.exec` and `man systemd.resource-control` for an explanation
# of the various systemd options available through this module. # of the various systemd options available through this module.
lib: lib: pkgs:
with lib; with lib;
{ {
# These settings roughly follow systemd's "strict" security profile
defaultHardening = { defaultHardening = {
PrivateTmp = "true"; PrivateTmp = "true";
ProtectSystem = "full"; ProtectSystem = "strict";
ProtectHome = "true"; ProtectHome = "true";
NoNewPrivileges = "true"; NoNewPrivileges = "true";
PrivateDevices = "true"; PrivateDevices = "true";
@ -19,7 +20,15 @@ with lib;
RestrictNamespaces = "true"; RestrictNamespaces = "true";
LockPersonality = "true"; LockPersonality = "true";
IPAddressDeny = "any"; IPAddressDeny = "any";
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"; PrivateUsers = "true";
RestrictSUIDSGID = "true";
RemoveIPC = "true";
RestrictRealtime = "true";
ProtectHostname = "true";
CapabilityBoundingSet = "";
# @system-service whitelist and docker seccomp blacklist (except for "clone"
# which is a core requirement for systemd services)
SystemCallFilter = [ "@system-service" "~add_key clone3 get_mempolicy kcmp keyctl mbind move_pages name_to_handle_at personality process_vm_readv process_vm_writev request_key set_mempolicy setns unshare userfaultfd" ];
SystemCallArchitectures= "native"; SystemCallArchitectures= "native";
}; };
@ -41,4 +50,9 @@ with lib;
to 127.0.0.1;"; to 127.0.0.1;";
''; '';
}; };
script = src: pkgs.writers.writeBash "script" ''
set -eo pipefail
${src}
'';
} }

View File

@ -28,9 +28,8 @@ let
''; '';
createWebIndex = pkgs.writeText "make-index.sh" '' createWebIndex = pkgs.writeText "make-index.sh" ''
set -e set -e
mkdir -p /var/www/
cp ${indexFile} /var/www/index.html cp ${indexFile} /var/www/index.html
chown -R nginx /var/www/ chown -R nginx:nginx /var/www/
nodeinfo nodeinfo
. <(nodeinfo) . <(nodeinfo)
sed -i "s/CLIGHTNING_ID/$CLIGHTNING_ID/g" /var/www/index.html sed -i "s/CLIGHTNING_ID/$CLIGHTNING_ID/g" /var/www/index.html
@ -48,6 +47,10 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d /var/www 0755 nginx nginx - -"
];
services.nginx = { services.nginx = {
enable = true; enable = true;
virtualHosts."_" = { virtualHosts."_" = {
@ -81,15 +84,18 @@ in {
jq jq
sudo sudo
]; ];
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
ExecStart="${pkgs.bash}/bin/bash ${createWebIndex}"; ExecStart="${pkgs.bash}/bin/bash ${createWebIndex}";
User = "root"; User = "root";
Type = "simple"; Type = "simple";
RemainAfterExit="yes"; RemainAfterExit="yes";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening PrivateNetwork = "true"; # This service needs no network access
// (if cfg.enforceTor PrivateUsers = "false";
ReadWritePaths = "/var/www";
CapabilityBoundingSet = "CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_SYS_ADMIN CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER";
} // (if cfg.enforceTor
then nix-bitcoin-services.allowTor then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP else nix-bitcoin-services.allowAnyIP
); );

View File

@ -15,7 +15,6 @@ let
# wait until tor is up # wait until tor is up
until ls -l /var/lib/tor/state; do sleep 1; done until ls -l /var/lib/tor/state; do sleep 1; done
mkdir -p -m 0755 ${dataDir}
cd ${dataDir} cd ${dataDir}
# Create directory for every user and set permissions # Create directory for every user and set permissions
@ -68,16 +67,24 @@ in {
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
systemd.tmpfiles.rules = [
"d '${dataDir}' 0755 root root - -"
];
systemd.services.onion-chef = { systemd.services.onion-chef = {
description = "Run onion-chef"; description = "Run onion-chef";
wantedBy = [ "tor.service" ]; wantedBy = [ "tor.service" ];
bindsTo = [ "tor.service" ]; bindsTo = [ "tor.service" ];
after = [ "tor.service" ]; after = [ "tor.service" ];
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}"; ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}";
Type = "oneshot"; Type = "oneshot";
RemainAfterExit = true; RemainAfterExit = true;
} // nix-bitcoin-services.defaultHardening; PrivateNetwork = "true"; # This service needs no network access
PrivateUsers = "false";
ReadWritePaths = "${dataDir}";
CapabilityBoundingSet = "CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER";
};
}; };
}; };
} }

View File

@ -60,8 +60,6 @@ in {
proxy = cfg.tor.client.socksListenAddress; proxy = cfg.tor.client.socksListenAddress;
enforceTor = true; enforceTor = true;
port = 8333; port = 8333;
zmqpubrawblock = "tcp://127.0.0.1:28332";
zmqpubrawtx = "tcp://127.0.0.1:28333";
assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6"; assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6";
addnodes = [ "ecoc5q34tmbq54wl.onion" ]; addnodes = [ "ecoc5q34tmbq54wl.onion" ];
discover = false; discover = false;

View File

@ -90,12 +90,11 @@ in {
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];
after = [ "clightning.service" ]; after = [ "clightning.service" ];
path = with pkgs; [ nix-bitcoin.clightning curl torsocks sudo jq ]; path = with pkgs; [ nix-bitcoin.clightning curl torsocks sudo jq ];
serviceConfig = { serviceConfig = nix-bitcoin-services.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";
} // nix-bitcoin-services.defaultHardening } // nix-bitcoin-services.allowTor;
// nix-bitcoin-services.allowTor;
}; };
systemd.timers.recurring-donations = { systemd.timers.recurring-donations = {
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];

View File

@ -5,7 +5,6 @@ with lib;
let let
cfg = config.services.spark-wallet; cfg = config.services.spark-wallet;
inherit (config) nix-bitcoin-services; inherit (config) nix-bitcoin-services;
dataDir = "/var/lib/spark-wallet/";
onion-chef-service = (if cfg.onion-service then [ "onion-chef.service" ] else []); onion-chef-service = (if cfg.onion-service then [ "onion-chef.service" ] else []);
run-spark-wallet = pkgs.writeScript "run-spark-wallet" '' run-spark-wallet = pkgs.writeScript "run-spark-wallet" ''
CMD="${pkgs.nix-bitcoin.spark-wallet}/bin/spark-wallet --ln-path ${cfg.ln-path} -Q -k -c ${config.nix-bitcoin.secretsDir}/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"
@ -71,14 +70,13 @@ in {
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
requires = [ "clightning.service" ] ++ onion-chef-service; requires = [ "clightning.service" ] ++ onion-chef-service;
after = [ "clightning.service" ] ++ onion-chef-service; after = [ "clightning.service" ] ++ onion-chef-service;
serviceConfig = { serviceConfig = nix-bitcoin-services.defaultHardening // {
PermissionsStartOnly = "true";
ExecStart = "${pkgs.bash}/bin/bash ${run-spark-wallet}"; ExecStart = "${pkgs.bash}/bin/bash ${run-spark-wallet}";
User = "spark-wallet"; User = "spark-wallet";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
} // nix-bitcoin-services.defaultHardening ReadWritePaths = "/var/lib/onion-chef";
// nix-bitcoin-services.nodejs } // nix-bitcoin-services.nodejs
// nix-bitcoin-services.allowTor; // nix-bitcoin-services.allowTor;
}; };
nix-bitcoin.secrets.spark-wallet-login.user = "spark-wallet"; nix-bitcoin.secrets.spark-wallet-login.user = "spark-wallet";