From 7c70dd43acb1fdb75d43d0bcef570e3daffeb1e4 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Tue, 5 May 2020 15:18:41 +0200 Subject: [PATCH 01/12] All modules: Give service config precedence over defaultHardening With '//' the latter takes precedence over the former in case of equally named attributes. --- modules/bitcoind.nix | 10 ++++------ modules/clightning.nix | 5 ++--- modules/electrs.nix | 5 ++--- modules/lightning-charge.nix | 5 ++--- modules/liquid.nix | 5 ++--- modules/lnd.nix | 5 ++--- modules/nanopos.nix | 5 ++--- modules/nix-bitcoin-webindex.nix | 5 ++--- modules/onion-chef.nix | 4 ++-- modules/recurring-donations.nix | 5 ++--- modules/spark-wallet.nix | 5 ++--- 11 files changed, 24 insertions(+), 35 deletions(-) diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index 7d9d031..1f3a7ae 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -282,7 +282,7 @@ in { sleep 0.05 done ''; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { User = "${cfg.user}"; Group = "${cfg.group}"; ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'"; @@ -291,8 +291,7 @@ in { # Permission for preStart PermissionsStartOnly = "true"; - } // nix-bitcoin-services.defaultHardening - // (if cfg.enforceTor + } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP) // optionalAttrs (cfg.zmqpubrawblock != null || cfg.zmqpubrawtx != null) nix-bitcoin-services.allowAnyProtocol; @@ -320,11 +319,10 @@ in { fi done ''; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { User = "${cfg.user}"; Group = "${cfg.group}"; - } // nix-bitcoin-services.defaultHardening - // nix-bitcoin-services.allowTor; + } // nix-bitcoin-services.allowTor; }; users.users.${cfg.user} = { diff --git a/modules/clightning.nix b/modules/clightning.nix index 4a08ae0..602b635 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -93,14 +93,13 @@ in { chmod 600 ${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}"; User = "clightning"; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // (if cfg.enforceTor + } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP ); diff --git a/modules/electrs.nix b/modules/electrs.nix index 5b62149..1c6df3b 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -74,7 +74,7 @@ in { echo "cookie = \"${config.services.bitcoind.rpcuser}:$(cat ${secretsDir}/bitcoin-rpcpassword)\"" \ > electrs.toml ''; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { RuntimeDirectory = "electrs"; RuntimeDirectoryMode = "700"; WorkingDirectory = "/run/electrs"; @@ -96,8 +96,7 @@ in { Group = cfg.group; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // (if cfg.enforceTor + } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP ); diff --git a/modules/lightning-charge.nix b/modules/lightning-charge.nix index 0da37c3..ccf11bc 100644 --- a/modules/lightning-charge.nix +++ b/modules/lightning-charge.nix @@ -50,15 +50,14 @@ in { chmod 600 ${cfg.dataDir}/lightning-charge.db fi ''; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { PermissionsStartOnly = "true"; 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"; User = user; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // nix-bitcoin-services.nodejs + } // nix-bitcoin-services.nodejs // nix-bitcoin-services.allowTor; }; nix-bitcoin.secrets.lightning-charge-env.user = user; diff --git a/modules/liquid.nix b/modules/liquid.nix index 2bb604e..d5a7413 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -215,7 +215,7 @@ in { echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf' echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/elements.conf' ''; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { Type = "simple"; User = "${cfg.user}"; Group = "${cfg.group}"; @@ -226,8 +226,7 @@ in { # Permission for preStart PermissionsStartOnly = "true"; - } // nix-bitcoin-services.defaultHardening - // (if cfg.enforceTor + } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP ); diff --git a/modules/lnd.nix b/modules/lnd.nix index 3df08b6..d5d41e3 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -91,14 +91,13 @@ in { chmod u=rw,g=r,o= ${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"; User = "lnd"; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // (if cfg.enforceTor + } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP ) // nix-bitcoin-services.allowAnyProtocol; # For ZMQ diff --git a/modules/nanopos.nix b/modules/nanopos.nix index 2fa5894..6cc1529 100644 --- a/modules/nanopos.nix +++ b/modules/nanopos.nix @@ -58,14 +58,13 @@ in { wantedBy = [ "multi-user.target" ]; requires = [ "lightning-charge.service" ]; after = [ "lightning-charge.service" ]; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { 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"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // nix-bitcoin-services.nodejs + } // nix-bitcoin-services.nodejs // nix-bitcoin-services.allowTor; }; users.users.nanopos = { diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix index 6eb8a02..be64f6d 100644 --- a/modules/nix-bitcoin-webindex.nix +++ b/modules/nix-bitcoin-webindex.nix @@ -81,15 +81,14 @@ in { jq sudo ]; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { ExecStart="${pkgs.bash}/bin/bash ${createWebIndex}"; User = "root"; Type = "simple"; RemainAfterExit="yes"; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // (if cfg.enforceTor + } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP ); diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix index df9c36f..a5084a3 100644 --- a/modules/onion-chef.nix +++ b/modules/onion-chef.nix @@ -73,11 +73,11 @@ in { wantedBy = [ "tor.service" ]; bindsTo = [ "tor.service" ]; after = [ "tor.service" ]; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}"; Type = "oneshot"; RemainAfterExit = true; - } // nix-bitcoin-services.defaultHardening; + }; }; }; } diff --git a/modules/recurring-donations.nix b/modules/recurring-donations.nix index dcc61a6..659891a 100644 --- a/modules/recurring-donations.nix +++ b/modules/recurring-donations.nix @@ -90,12 +90,11 @@ in { requires = [ "clightning.service" ]; after = [ "clightning.service" ]; path = with pkgs; [ nix-bitcoin.clightning curl torsocks sudo jq ]; - serviceConfig = { + serviceConfig = nix-bitcoin-services.defaultHardening // { ExecStart = "${pkgs.bash}/bin/bash ${recurring-donations-script}"; User = "recurring-donations"; Type = "oneshot"; - } // nix-bitcoin-services.defaultHardening - // nix-bitcoin-services.allowTor; + } // nix-bitcoin-services.allowTor; }; systemd.timers.recurring-donations = { requires = [ "clightning.service" ]; diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index a4b5319..8c506a1 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -71,14 +71,13 @@ in { wantedBy = [ "multi-user.target" ]; requires = [ "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}"; User = "spark-wallet"; Restart = "on-failure"; RestartSec = "10s"; - } // nix-bitcoin-services.defaultHardening - // nix-bitcoin-services.nodejs + } // nix-bitcoin-services.nodejs // nix-bitcoin-services.allowTor; }; nix-bitcoin.secrets.spark-wallet-login.user = "spark-wallet"; From 3cd61506e021df2df3f1a170724c6f11f0d54a88 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Tue, 5 May 2020 15:25:00 +0200 Subject: [PATCH 02/12] webindex & onion-chef: Run non-network-facing services in PrivateNetwork --- modules/nix-bitcoin-webindex.nix | 1 + modules/onion-chef.nix | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix index be64f6d..95ee066 100644 --- a/modules/nix-bitcoin-webindex.nix +++ b/modules/nix-bitcoin-webindex.nix @@ -88,6 +88,7 @@ in { RemainAfterExit="yes"; Restart = "on-failure"; RestartSec = "10s"; + PrivateNetwork = "true"; # This service needs no network access } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix index a5084a3..a4b025b 100644 --- a/modules/onion-chef.nix +++ b/modules/onion-chef.nix @@ -77,6 +77,7 @@ in { ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}"; Type = "oneshot"; RemainAfterExit = true; + PrivateNetwork = "true"; # This service needs no network access }; }; }; From 81a1c3f9088f57ae9125fa6e32a04240cafb9dd3 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Tue, 5 May 2020 15:27:07 +0200 Subject: [PATCH 03/12] service hardening: Add CapabilityBoundingSets Whitelist with exceptions in webindex and onion-chef --- modules/nix-bitcoin-services.nix | 1 + modules/nix-bitcoin-webindex.nix | 1 + modules/onion-chef.nix | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/nix-bitcoin-services.nix b/modules/nix-bitcoin-services.nix index c04e14c..dba6dd3 100644 --- a/modules/nix-bitcoin-services.nix +++ b/modules/nix-bitcoin-services.nix @@ -19,6 +19,7 @@ with lib; RestrictNamespaces = "true"; LockPersonality = "true"; IPAddressDeny = "any"; + CapabilityBoundingSet = ""; 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"; }; diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix index 95ee066..d3a1939 100644 --- a/modules/nix-bitcoin-webindex.nix +++ b/modules/nix-bitcoin-webindex.nix @@ -89,6 +89,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; PrivateNetwork = "true"; # This service needs no network access + 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 else nix-bitcoin-services.allowAnyIP diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix index a4b025b..9fabd5a 100644 --- a/modules/onion-chef.nix +++ b/modules/onion-chef.nix @@ -78,6 +78,7 @@ in { Type = "oneshot"; RemainAfterExit = true; PrivateNetwork = "true"; # This service needs no network access + CapabilityBoundingSet = "CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER"; }; }; }; From 423ebf862b2cf0731ee7602fe8a26697e06fbdb4 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Tue, 5 May 2020 16:28:30 +0200 Subject: [PATCH 04/12] lnd: only enable bitcoind zmqpub if lnd.enable In conjuction with secure-node.nix, this sets sane RestrictAddressFamilies unless lnd is enabled. Before, we were constantly exposing unnecessary Address Families, not just when lnd is enabled. However, zmqpub* must always be enabled for lnd, even when used outside of secure-node.nix, so we make this change in the lnd module. --- modules/lnd.nix | 6 ++++++ modules/presets/secure-node.nix | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/modules/lnd.nix b/modules/lnd.nix index d5d41e3..03fbd93 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -78,6 +78,12 @@ in { config = mkIf cfg.enable { environment.systemPackages = [ cfg.package (hiPrio cfg.cli) ]; + + services.bitcoind = { + zmqpubrawblock = "tcp://127.0.0.1:28332"; + zmqpubrawtx = "tcp://127.0.0.1:28333"; + }; + systemd.services.lnd = { description = "Run LND"; path = [ pkgs.nix-bitcoin.bitcoind ]; diff --git a/modules/presets/secure-node.nix b/modules/presets/secure-node.nix index d010c0f..7e1704f 100644 --- a/modules/presets/secure-node.nix +++ b/modules/presets/secure-node.nix @@ -60,8 +60,6 @@ in { proxy = cfg.tor.client.socksListenAddress; enforceTor = true; port = 8333; - zmqpubrawblock = "tcp://127.0.0.1:28332"; - zmqpubrawtx = "tcp://127.0.0.1:28333"; assumevalid = "00000000000000000000e5abc3a74fe27dc0ead9c70ea1deb456f11c15fd7bc6"; addnodes = [ "ecoc5q34tmbq54wl.onion" ]; discover = false; From 91b6b2c370f5a5c4ff711b345447decb7c5e3333 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Wed, 6 May 2020 12:43:57 +0200 Subject: [PATCH 05/12] All modules with preStart: Use systemd.tmpfiles.rules This is NixOS' recommended way to setup service dirs https://github.com/NixOS/nixpkgs/pull/56265. This commit hands off the initial data directory creation to systemd.tmpfiles.rules. All other preStart scripts are left intact to limit this changes' scope. --- modules/bitcoind.nix | 12 +++++------- modules/clightning.nix | 5 ++++- modules/electrs.nix | 6 ++++-- modules/liquid.nix | 8 +++++--- modules/lnd.nix | 5 ++++- modules/nix-bitcoin-webindex.nix | 7 +++++-- modules/onion-chef.nix | 5 ++++- modules/spark-wallet.nix | 1 - 8 files changed, 31 insertions(+), 18 deletions(-) diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index 1f3a7ae..f1f1af0 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -255,19 +255,17 @@ in { 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 = { description = "Bitcoin daemon"; requires = [ "nix-bitcoin-secrets.target" ]; after = [ "network.target" "nix-bitcoin-secrets.target" ]; wantedBy = [ "multi-user.target" ]; 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'"} cfg=$(cat ${configFile}; printf "rpcpassword="; cat "${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword") diff --git a/modules/clightning.nix b/modules/clightning.nix index 602b635..39b84a2 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -78,6 +78,10 @@ in { }; users.groups.clightning = {}; + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' 0770 ${config.users.users.clightning.name} ${config.users.users.clightning.group} - -" + ]; + systemd.services.clightning = { description = "Run clightningd"; path = [ pkgs.nix-bitcoin.bitcoind ]; @@ -85,7 +89,6 @@ in { requires = [ "bitcoind.service" ]; after = [ "bitcoind.service" ]; preStart = '' - mkdir -m 0770 -p ${cfg.dataDir} cp ${configFile} ${cfg.dataDir}/config chown -R 'clightning:clightning' '${cfg.dataDir}' # The RPC socket has to be removed otherwise we might have stale sockets diff --git a/modules/electrs.nix b/modules/electrs.nix index 1c6df3b..15f4f8d 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -63,14 +63,16 @@ in { config = mkIf cfg.enable (mkMerge [{ environment.systemPackages = [ pkgs.nix-bitcoin.electrs ]; + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" + ]; + systemd.services.electrs = { description = "Electrs Electrum Server"; wantedBy = [ "multi-user.target" ]; requires = [ "bitcoind.service" ]; after = [ "bitcoind.service" ]; 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)\"" \ > electrs.toml ''; diff --git a/modules/liquid.nix b/modules/liquid.nix index d5a7413..7fe6674 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -200,15 +200,17 @@ in { (hiPrio cfg.cli) (hiPrio cfg.swap-cli) ]; + + systemd.tmpfiles.rules = [ + "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" + ]; + systemd.services.liquidd = { description = "Elements daemon providing access to the Liquid sidechain"; requires = [ "bitcoind.service" ]; after = [ "bitcoind.service" ]; wantedBy = [ "multi-user.target" ]; preStart = '' - if ! test -e ${cfg.dataDir}; then - mkdir -m 0770 -p '${cfg.dataDir}' - fi cp '${configFile}' '${cfg.dataDir}/elements.conf' chmod o-rw '${cfg.dataDir}/elements.conf' chown -R '${cfg.user}:${cfg.group}' '${cfg.dataDir}' diff --git a/modules/lnd.nix b/modules/lnd.nix index 03fbd93..2da8c50 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -79,6 +79,10 @@ in { config = mkIf cfg.enable { 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"; @@ -91,7 +95,6 @@ in { requires = [ "bitcoind.service" ]; after = [ "bitcoind.service" ]; preStart = '' - mkdir -m 0770 -p ${cfg.dataDir} cp ${configFile} ${cfg.dataDir}/lnd.conf chown -R 'lnd:lnd' '${cfg.dataDir}' chmod u=rw,g=r,o= ${cfg.dataDir}/lnd.conf diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix index d3a1939..7429500 100644 --- a/modules/nix-bitcoin-webindex.nix +++ b/modules/nix-bitcoin-webindex.nix @@ -28,9 +28,8 @@ let ''; createWebIndex = pkgs.writeText "make-index.sh" '' set -e - mkdir -p /var/www/ cp ${indexFile} /var/www/index.html - chown -R nginx /var/www/ + chown -R nginx:nginx /var/www/ nodeinfo . <(nodeinfo) sed -i "s/CLIGHTNING_ID/$CLIGHTNING_ID/g" /var/www/index.html @@ -48,6 +47,10 @@ in { }; config = mkIf cfg.enable { + systemd.tmpfiles.rules = [ + "d /var/www 0755 nginx nginx - -" + ]; + services.nginx = { enable = true; virtualHosts."_" = { diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix index 9fabd5a..86d035e 100644 --- a/modules/onion-chef.nix +++ b/modules/onion-chef.nix @@ -15,7 +15,6 @@ let # wait until tor is up until ls -l /var/lib/tor/state; do sleep 1; done - mkdir -p -m 0755 ${dataDir} cd ${dataDir} # Create directory for every user and set permissions @@ -68,6 +67,10 @@ in { }; config = mkIf cfg.enable { + systemd.tmpfiles.rules = [ + "d '${dataDir}' 0755 root root - -" + ]; + systemd.services.onion-chef = { description = "Run onion-chef"; wantedBy = [ "tor.service" ]; diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index 8c506a1..6488a56 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -5,7 +5,6 @@ with lib; let 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.nix-bitcoin.spark-wallet}/bin/spark-wallet --ln-path ${cfg.ln-path} -Q -k -c ${config.nix-bitcoin.secretsDir}/spark-wallet-login" From adc71b892e07ee71cece4205068d8fedbc1e4612 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Thu, 21 May 2020 18:05:31 +0200 Subject: [PATCH 06/12] Remove PermissionStartOnly where possible and replace with bitcoinrpc Remove PermissionsStartOnly for bitcoind and spark-wallet (it was never needed there) Give reason for PermissionsStartOnly in lightning-charge Replace PermissionsStartOnly in clightning, electrs and liquid --- modules/bitcoind.nix | 5 ++--- modules/clightning.nix | 2 +- modules/electrs.nix | 3 +-- modules/lightning-charge.nix | 1 + modules/liquid.nix | 7 ++----- modules/lnd.nix | 1 + modules/spark-wallet.nix | 1 - 7 files changed, 8 insertions(+), 12 deletions(-) diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index f1f1af0..74323cb 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -286,9 +286,6 @@ in { ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'"; Restart = "on-failure"; UMask = mkIf cfg.dataDirReadableByGroup "0027"; - - # Permission for preStart - PermissionsStartOnly = "true"; } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP) @@ -328,9 +325,11 @@ in { description = "Bitcoin daemon user"; }; users.groups.${cfg.group} = {}; + users.groups.bitcoinrpc = {}; nix-bitcoin.secrets.bitcoin-rpcpassword = { user = "bitcoin"; + group = "bitcoinrpc"; }; }; } diff --git a/modules/clightning.nix b/modules/clightning.nix index 39b84a2..501b4c9 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -75,6 +75,7 @@ in { users.users.clightning = { description = "clightning User"; group = "clightning"; + extraGroups = [ "bitcoinrpc" ]; }; users.groups.clightning = {}; @@ -97,7 +98,6 @@ in { echo "bitcoin-rpcpassword=$(cat ${config.nix-bitcoin.secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/config' ''; serviceConfig = nix-bitcoin-services.defaultHardening // { - PermissionsStartOnly = "true"; ExecStart = "${pkgs.nix-bitcoin.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; User = "clightning"; Restart = "on-failure"; diff --git a/modules/electrs.nix b/modules/electrs.nix index 15f4f8d..379ea0f 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -80,7 +80,6 @@ in { RuntimeDirectory = "electrs"; RuntimeDirectoryMode = "700"; WorkingDirectory = "/run/electrs"; - PermissionsStartOnly = "true"; ExecStart = '' ${pkgs.nix-bitcoin.electrs}/bin/electrs -vvv \ ${if cfg.high-memory then @@ -107,7 +106,7 @@ in { users.users.${cfg.user} = { description = "electrs User"; group = cfg.group; - extraGroups = optionals cfg.high-memory [ "bitcoin" ]; + extraGroups = [ "bitcoinrpc" ] ++ optionals cfg.high-memory [ "bitcoin" ]; }; users.groups.${cfg.group} = {}; } diff --git a/modules/lightning-charge.nix b/modules/lightning-charge.nix index ccf11bc..aa392ed 100644 --- a/modules/lightning-charge.nix +++ b/modules/lightning-charge.nix @@ -51,6 +51,7 @@ in { fi ''; serviceConfig = nix-bitcoin-services.defaultHardening // { + # Needed to access clightning.dataDir in preStart PermissionsStartOnly = "true"; 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"; diff --git a/modules/liquid.nix b/modules/liquid.nix index 7fe6674..bbd45fb 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -212,7 +212,7 @@ in { wantedBy = [ "multi-user.target" ]; preStart = '' 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}' echo "rpcpassword=$(cat ${secretsDir}/liquid-rpcpassword)" >> '${cfg.dataDir}/elements.conf' echo "mainchainrpcpassword=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/elements.conf' @@ -222,12 +222,8 @@ in { User = "${cfg.user}"; Group = "${cfg.group}"; ExecStart = "${pkgs.nix-bitcoin.elementsd}/bin/elementsd ${cmdlineOptions}"; - StateDirectory = "liquidd"; PIDFile = "${pidFile}"; Restart = "on-failure"; - - # Permission for preStart - PermissionsStartOnly = "true"; } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP @@ -235,6 +231,7 @@ in { }; users.users.${cfg.user} = { group = cfg.group; + extraGroups = [ "bitcoinrpc" ]; description = "Liquid sidechain user"; }; users.groups.${cfg.group} = {}; diff --git a/modules/lnd.nix b/modules/lnd.nix index 2da8c50..8554db6 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -163,6 +163,7 @@ in { users.users.lnd = { description = "LND User"; group = "lnd"; + extraGroups = [ "bitcoinrpc" ]; home = cfg.dataDir; # lnd creates .lnd dir in HOME }; users.groups.lnd = {}; diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index 6488a56..f1c3179 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -71,7 +71,6 @@ in { requires = [ "clightning.service" ] ++ onion-chef-service; after = [ "clightning.service" ] ++ onion-chef-service; serviceConfig = nix-bitcoin-services.defaultHardening // { - PermissionsStartOnly = "true"; ExecStart = "${pkgs.bash}/bin/bash ${run-spark-wallet}"; User = "spark-wallet"; Restart = "on-failure"; From a040e5285432452af613b4b72a65e852e63c31b6 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Tue, 5 May 2020 17:15:16 +0200 Subject: [PATCH 07/12] All modules: ProtectSystem = strict Add ReadWritePaths in all modules, except lnd which has ProtectSystem = full. --- modules/bitcoind.nix | 2 ++ modules/clightning.nix | 1 + modules/electrs.nix | 1 + modules/lightning-charge.nix | 1 + modules/liquid.nix | 1 + modules/lnd.nix | 1 + modules/nix-bitcoin-services.nix | 2 +- modules/nix-bitcoin-webindex.nix | 1 + modules/onion-chef.nix | 1 + modules/spark-wallet.nix | 1 + 10 files changed, 11 insertions(+), 1 deletion(-) diff --git a/modules/bitcoind.nix b/modules/bitcoind.nix index 74323cb..6f2dc1c 100644 --- a/modules/bitcoind.nix +++ b/modules/bitcoind.nix @@ -286,6 +286,7 @@ in { ExecStart = "${cfg.package}/bin/bitcoind -datadir='${cfg.dataDir}'"; Restart = "on-failure"; UMask = mkIf cfg.dataDirReadableByGroup "0027"; + ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP) @@ -317,6 +318,7 @@ in { serviceConfig = nix-bitcoin-services.defaultHardening // { User = "${cfg.user}"; Group = "${cfg.group}"; + ReadWritePaths = "${cfg.dataDir}"; } // nix-bitcoin-services.allowTor; }; diff --git a/modules/clightning.nix b/modules/clightning.nix index 501b4c9..e513fa9 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -102,6 +102,7 @@ in { User = "clightning"; Restart = "on-failure"; RestartSec = "10s"; + ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP diff --git a/modules/electrs.nix b/modules/electrs.nix index 379ea0f..4c2b95f 100644 --- a/modules/electrs.nix +++ b/modules/electrs.nix @@ -97,6 +97,7 @@ in { Group = cfg.group; Restart = "on-failure"; RestartSec = "10s"; + ReadWritePaths = "${cfg.dataDir} ${if cfg.high-memory then "${config.services.bitcoind.dataDir}" else ""}"; } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP diff --git a/modules/lightning-charge.nix b/modules/lightning-charge.nix index aa392ed..d5d1d67 100644 --- a/modules/lightning-charge.nix +++ b/modules/lightning-charge.nix @@ -58,6 +58,7 @@ in { User = user; Restart = "on-failure"; RestartSec = "10s"; + ReadWritePaths = "${cfg.dataDir}"; } // nix-bitcoin-services.nodejs // nix-bitcoin-services.allowTor; }; diff --git a/modules/liquid.nix b/modules/liquid.nix index bbd45fb..d943eb2 100644 --- a/modules/liquid.nix +++ b/modules/liquid.nix @@ -224,6 +224,7 @@ in { ExecStart = "${pkgs.nix-bitcoin.elementsd}/bin/elementsd ${cmdlineOptions}"; PIDFile = "${pidFile}"; Restart = "on-failure"; + ReadWritePaths = "${cfg.dataDir}"; } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP diff --git a/modules/lnd.nix b/modules/lnd.nix index 8554db6..720dcb9 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -106,6 +106,7 @@ in { User = "lnd"; Restart = "on-failure"; RestartSec = "10s"; + ProtectSystem = "full"; # ToDo: Make more restrictive } // (if cfg.enforceTor then nix-bitcoin-services.allowTor else nix-bitcoin-services.allowAnyIP diff --git a/modules/nix-bitcoin-services.nix b/modules/nix-bitcoin-services.nix index dba6dd3..e899bd9 100644 --- a/modules/nix-bitcoin-services.nix +++ b/modules/nix-bitcoin-services.nix @@ -7,7 +7,7 @@ with lib; { defaultHardening = { PrivateTmp = "true"; - ProtectSystem = "full"; + ProtectSystem = "strict"; ProtectHome = "true"; NoNewPrivileges = "true"; PrivateDevices = "true"; diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix index 7429500..4f756c3 100644 --- a/modules/nix-bitcoin-webindex.nix +++ b/modules/nix-bitcoin-webindex.nix @@ -92,6 +92,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; PrivateNetwork = "true"; # This service needs no network access + 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 diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix index 86d035e..dcc4ea2 100644 --- a/modules/onion-chef.nix +++ b/modules/onion-chef.nix @@ -81,6 +81,7 @@ in { Type = "oneshot"; RemainAfterExit = true; PrivateNetwork = "true"; # This service needs no network access + ReadWritePaths = "${dataDir}"; CapabilityBoundingSet = "CAP_CHOWN CAP_FSETID CAP_SETFCAP CAP_DAC_OVERRIDE CAP_DAC_READ_SEARCH CAP_FOWNER CAP_IPC_OWNER"; }; }; diff --git a/modules/spark-wallet.nix b/modules/spark-wallet.nix index f1c3179..a329def 100644 --- a/modules/spark-wallet.nix +++ b/modules/spark-wallet.nix @@ -75,6 +75,7 @@ in { User = "spark-wallet"; Restart = "on-failure"; RestartSec = "10s"; + ReadWritePaths = "/var/lib/onion-chef"; } // nix-bitcoin-services.nodejs // nix-bitcoin-services.allowTor; }; From 5f3f36245150ad0cb79c2ed3858d13839c7037dd Mon Sep 17 00:00:00 2001 From: Erik Arvstedt Date: Fri, 22 May 2020 15:59:18 +0200 Subject: [PATCH 08/12] lnd: add strict hardening Add ProtectSystem=strict, remove PermissionStartOnly. Extract the section of postStart that needs secrets dir write access into a separate script with full privileges. Simplify preStart and fix dataDir quoting. --- modules/lnd.nix | 110 ++++++++++++++++--------------- modules/modules.nix | 2 +- modules/nix-bitcoin-services.nix | 7 +- 3 files changed, 63 insertions(+), 56 deletions(-) diff --git a/modules/lnd.nix b/modules/lnd.nix index 720dcb9..a66d9a8 100644 --- a/modules/lnd.nix +++ b/modules/lnd.nix @@ -95,71 +95,73 @@ in { requires = [ "bitcoind.service" ]; after = [ "bitcoind.service" ]; preStart = '' - cp ${configFile} ${cfg.dataDir}/lnd.conf - chown -R 'lnd:lnd' '${cfg.dataDir}' - chmod u=rw,g=r,o= ${cfg.dataDir}/lnd.conf + install -m600 ${configFile} '${cfg.dataDir}/lnd.conf' echo "bitcoind.rpcpass=$(cat ${secretsDir}/bitcoin-rpcpassword)" >> '${cfg.dataDir}/lnd.conf' ''; serviceConfig = nix-bitcoin-services.defaultHardening // { - PermissionsStartOnly = "true"; ExecStart = "${cfg.package}/bin/lnd --configfile=${cfg.dataDir}/lnd.conf"; User = "lnd"; Restart = "on-failure"; RestartSec = "10s"; - ProtectSystem = "full"; # ToDo: Make more restrictive + ReadWritePaths = "${cfg.dataDir}"; + 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 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"; diff --git a/modules/modules.nix b/modules/modules.nix index 4c6d291..faf12a7 100644 --- a/modules/modules.nix +++ b/modules/modules.nix @@ -21,7 +21,7 @@ options = { nix-bitcoin-services = lib.mkOption { readOnly = true; - default = import ./nix-bitcoin-services.nix lib; + default = import ./nix-bitcoin-services.nix lib pkgs; }; }; diff --git a/modules/nix-bitcoin-services.nix b/modules/nix-bitcoin-services.nix index e899bd9..db875ba 100644 --- a/modules/nix-bitcoin-services.nix +++ b/modules/nix-bitcoin-services.nix @@ -1,7 +1,7 @@ # See `man systemd.exec` and `man systemd.resource-control` for an explanation # of the various systemd options available through this module. -lib: +lib: pkgs: with lib; { @@ -42,4 +42,9 @@ with lib; to 127.0.0.1;"; ''; }; + + script = src: pkgs.writers.writeBash "script" '' + set -eo pipefail + ${src} + ''; } From 1c75543f2f6683f72c550d09bb81418be124c73e Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Tue, 19 May 2020 16:09:22 +0000 Subject: [PATCH 09/12] clightning: add user and group options --- modules/clightning.nix | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/modules/clightning.nix b/modules/clightning.nix index e513fa9..1b6ffff 100644 --- a/modules/clightning.nix +++ b/modules/clightning.nix @@ -59,6 +59,16 @@ in { default = "/var/lib/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 { readOnly = true; default = pkgs.writeScriptBin "lightning-cli" @@ -72,15 +82,15 @@ in { config = mkIf cfg.enable { environment.systemPackages = [ pkgs.nix-bitcoin.clightning (hiPrio cfg.cli) ]; - users.users.clightning = { + users.users.${cfg.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 ${config.users.users.clightning.name} ${config.users.users.clightning.group} - -" + "d '${cfg.dataDir}' 0770 ${cfg.user} ${cfg.group} - -" ]; systemd.services.clightning = { @@ -91,7 +101,7 @@ in { after = [ "bitcoind.service" ]; preStart = '' 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 rm -f ${cfg.dataDir}/bitcoin/lightning-rpc chmod 600 ${cfg.dataDir}/config @@ -99,7 +109,7 @@ in { ''; serviceConfig = nix-bitcoin-services.defaultHardening // { ExecStart = "${pkgs.nix-bitcoin.clightning}/bin/lightningd --lightning-dir=${cfg.dataDir}"; - User = "clightning"; + User = "${cfg.user}"; Restart = "on-failure"; RestartSec = "10s"; ReadWritePaths = "${cfg.dataDir}"; From e34d1c884ed6a26ffbab523087ad57f59688b9c8 Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Wed, 6 May 2020 10:28:00 +0200 Subject: [PATCH 10/12] service hardening: Add PrivateUsers Exceptions in webindex & onion-chef --- modules/nix-bitcoin-services.nix | 1 + modules/nix-bitcoin-webindex.nix | 1 + modules/onion-chef.nix | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/nix-bitcoin-services.nix b/modules/nix-bitcoin-services.nix index db875ba..aad34d4 100644 --- a/modules/nix-bitcoin-services.nix +++ b/modules/nix-bitcoin-services.nix @@ -19,6 +19,7 @@ with lib; RestrictNamespaces = "true"; LockPersonality = "true"; IPAddressDeny = "any"; + PrivateUsers = "true"; CapabilityBoundingSet = ""; 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"; diff --git a/modules/nix-bitcoin-webindex.nix b/modules/nix-bitcoin-webindex.nix index 4f756c3..a857ffb 100644 --- a/modules/nix-bitcoin-webindex.nix +++ b/modules/nix-bitcoin-webindex.nix @@ -92,6 +92,7 @@ in { Restart = "on-failure"; RestartSec = "10s"; PrivateNetwork = "true"; # This service needs no network access + 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 diff --git a/modules/onion-chef.nix b/modules/onion-chef.nix index dcc4ea2..2fe3839 100644 --- a/modules/onion-chef.nix +++ b/modules/onion-chef.nix @@ -81,6 +81,7 @@ in { Type = "oneshot"; RemainAfterExit = true; 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"; }; From 3fbfa986354054493a0770f25cd3792b83031edd Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Wed, 6 May 2020 10:57:48 +0200 Subject: [PATCH 11/12] service hardening: replace obtuse SystemCallFilter with @system-service @system-service whitelist and additional https://docs-stage.docker.com/engine/security/seccomp/#significant-syscalls-blocked-by-the-default-profile blacklist. --- modules/nix-bitcoin-services.nix | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/nix-bitcoin-services.nix b/modules/nix-bitcoin-services.nix index aad34d4..e155a41 100644 --- a/modules/nix-bitcoin-services.nix +++ b/modules/nix-bitcoin-services.nix @@ -5,6 +5,7 @@ lib: pkgs: with lib; { + # These settings roughly follow systemd's "strict" security profile defaultHardening = { PrivateTmp = "true"; ProtectSystem = "strict"; @@ -21,7 +22,9 @@ with lib; IPAddressDeny = "any"; PrivateUsers = "true"; CapabilityBoundingSet = ""; - 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"; + # @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"; }; From ccc3a70344d6beb6eba5b9e13efcda721f244f7c Mon Sep 17 00:00:00 2001 From: nixbitcoin Date: Wed, 6 May 2020 10:19:14 +0200 Subject: [PATCH 12/12] service hardening: add more restrictions Add RestrictSUIDSGID Add RemoveIPC Add RestrictRealtime Add ProtectHostname --- modules/nix-bitcoin-services.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/nix-bitcoin-services.nix b/modules/nix-bitcoin-services.nix index e155a41..daf1355 100644 --- a/modules/nix-bitcoin-services.nix +++ b/modules/nix-bitcoin-services.nix @@ -21,6 +21,10 @@ with lib; LockPersonality = "true"; IPAddressDeny = "any"; 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)