Merge branch 'service-hardening'

This commit is contained in:
Jonas Nick 2019-05-03 15:51:38 +00:00
commit 54a6a3363e
No known key found for this signature in database
GPG Key ID: 4861DBF262123605
14 changed files with 123 additions and 58 deletions

View File

@ -1,7 +1,7 @@
nix-bitcoin nix-bitcoin
=== ===
Nix packages and nixos modules for easily installing Bitcoin nodes and higher layer protocols. Nix packages and nixos modules for easily installing Bitcoin nodes and higher layer protocols with an emphasis on security.
This is a work in progress - don't expect it to be bug free or secure. This is a work in progress - don't expect it to be bug free or secure.
The default configuration sets up a Bitcoin Core node and c-lightning. The user can enable spark-wallet in `configuration.nix` to make c-lightning accessible with a smartphone using spark-wallet. The default configuration sets up a Bitcoin Core node and c-lightning. The user can enable spark-wallet in `configuration.nix` to make c-lightning accessible with a smartphone using spark-wallet.
@ -49,6 +49,19 @@ The easiest way is to run `nix-shell` (on a Linux machine) in the nix-bitcoin di
Fix the FIXMEs in configuration.nix and deploy with nixops in nix-shell. Fix the FIXMEs in configuration.nix and deploy with nixops in nix-shell.
See [install.md](docs/install.md) for a detailed tutorial. See [install.md](docs/install.md) for a detailed tutorial.
Security
---
* Nix package manager, NixOS and packages can be built from source to reduce reliance on binary caches.
* Builds happen in a [sandboxed environment](https://nixos.org/nix/manual/).
* Packages dependencies are [pinned](pkgs/nixpkgs-pinned.nix). Most packages are built from the [nixos stable channel](https://github.com/NixOS/nixpkgs-channels/tree/nixos-19.03), with a few exceptions that are built from the nixpkgs unstable channel.
* nix-bitcoin merge commits are signed.
* nix-bitcoin is built with a [hardened kernel](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/profiles/hardened.nix) by default.
* Services operate with least privileges. They each have their own user and are restricted further with [systemd options](modules/nix-bitcoin-services.nix).
* There's a non-root user *operator* to interact with the various services.
Note that nix-bitcoin is still experimental.
Also, by design if the machine you're deploying *from* is insecure, there is nothing nix-bitcoin can do to protect itself.
Hardware requirements Hardware requirements
--- ---
* Disk space: 300 GB (235GB for Bitcoin blockchain + some room) * Disk space: 300 GB (235GB for Bitcoin blockchain + some room)

View File

@ -169,6 +169,10 @@ This is borrowed from the [NixOS manual](https://nixos.org/nixos/manual/index.ht
swapon /dev/sda2 swapon /dev/sda2
``` ```
4. Option 3: Set up encrypted partitions:
Follow the guide at https://gist.github.com/martijnvermaat/76f2e24d0239470dd71050358b4d5134.
5. Generate NixOS config 5. Generate NixOS config
``` ```

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.bitcoind; cfg = config.services.bitcoind;
pidFile = "${cfg.dataDir}/bitcoind.pid"; pidFile = "${cfg.dataDir}/bitcoind.pid";
configFile = pkgs.writeText "bitcoin.conf" '' configFile = pkgs.writeText "bitcoin.conf" ''
@ -192,6 +193,7 @@ in {
to stay under the specified target size in MiB) to stay under the specified target size in MiB)
''; '';
}; };
enforceTor = nix-bitcoin-services.enforceTor;
}; };
}; };
@ -235,7 +237,11 @@ in {
# Permission for preStart # Permission for preStart
PermissionsStartOnly = "true"; PermissionsStartOnly = "true";
}; } // nix-bitcoin-services.defaultHardening
// (if cfg.enforceTor
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
);
}; };
systemd.services.bitcoind-import-banlist = { systemd.services.bitcoind-import-banlist = {
description = "Bitcoin daemon banlist importer"; description = "Bitcoin daemon banlist importer";
@ -269,16 +275,10 @@ in {
ExecStart = "${pkgs.bash}/bin/bash ${pkgs.banlist}/bin/banlist ${pkgs.altcoins.bitcoind}"; ExecStart = "${pkgs.bash}/bin/bash ${pkgs.banlist}/bin/banlist ${pkgs.altcoins.bitcoind}";
StateDirectory = "bitcoind"; StateDirectory = "bitcoind";
# Hardening measures
PrivateTmp = "true";
ProtectSystem = "full";
NoNewPrivileges = "true";
PrivateDevices = "true";
MemoryDenyWriteExecute = "true";
# Permission for preStart # Permission for preStart
PermissionsStartOnly = "true"; PermissionsStartOnly = "true";
}; } // nix-bitcoin-services.defaultHardening
// nix-bitcoin-services.allowTor;
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.clightning; cfg = config.services.clightning;
configFile = pkgs.writeText "config" '' configFile = pkgs.writeText "config" ''
autolisten=${if cfg.autolisten then "true" else "false"} autolisten=${if cfg.autolisten then "true" else "false"}
@ -56,6 +57,7 @@ in {
default = "/var/lib/clightning"; default = "/var/lib/clightning";
description = "The data directory for clightning."; description = "The data directory for clightning.";
}; };
enforceTor = nix-bitcoin-services.enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -93,12 +95,11 @@ in {
User = "clightning"; User = "clightning";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening
ProtectSystem = "full"; // (if cfg.enforceTor
NoNewPrivileges = "true"; then nix-bitcoin-services.allowTor
PrivateDevices = "true"; else nix-bitcoin-services.allowAnyIP
MemoryDenyWriteExecute = "true"; );
};
}; };
}; };
} }

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.electrs; cfg = config.services.electrs;
index-batch-size = "${if cfg.high-memory then "" else "--index-batch-size=10"}"; index-batch-size = "${if cfg.high-memory then "" else "--index-batch-size=10"}";
jsonrpc-import = "${if cfg.high-memory then "" else "--jsonrpc-import"}"; jsonrpc-import = "${if cfg.high-memory then "" else "--jsonrpc-import"}";
@ -42,6 +43,7 @@ in {
default = 50003; default = 50003;
description = "Override the default port on which to listen for connections."; description = "Override the default port on which to listen for connections.";
}; };
enforceTor = nix-bitcoin-services.enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -74,11 +76,11 @@ in {
User = "electrs"; User = "electrs";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening
ProtectSystem = "full"; // (if cfg.enforceTor
NoNewPrivileges = "true"; then nix-bitcoin-services.allowTor
PrivateDevices = "true"; else nix-bitcoin-services.allowAnyIP
}; );
}; };
services.nginx = { services.nginx = {

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.lightning-charge; cfg = config.services.lightning-charge;
in { in {
options.services.lightning-charge = { options.services.lightning-charge = {
@ -37,11 +38,9 @@ in {
User = "clightning"; User = "clightning";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening
ProtectSystem = "full"; // nix-bitcoin-services.nodejs
NoNewPrivileges = "true"; // nix-bitcoin-services.allowTor;
PrivateDevices = "true";
};
}; };
}; };
} }

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.liquidd; cfg = config.services.liquidd;
pidFile = "${cfg.dataDir}/liquidd.pid"; pidFile = "${cfg.dataDir}/liquidd.pid";
configFile = pkgs.writeText "liquid.conf" '' configFile = pkgs.writeText "liquid.conf" ''
@ -165,6 +166,7 @@ in {
to stay under the specified target size in MiB) to stay under the specified target size in MiB)
''; '';
}; };
enforceTor = nix-bitcoin-services.enforceTor;
}; };
}; };
@ -195,20 +197,16 @@ in {
PIDFile = "${pidFile}"; PIDFile = "${pidFile}";
Restart = "on-failure"; Restart = "on-failure";
# Hardening measures
PrivateTmp = "true";
ProtectSystem = "full";
NoNewPrivileges = "true";
PrivateDevices = "true";
MemoryDenyWriteExecute = "true";
# Permission for preStart # Permission for preStart
PermissionsStartOnly = "true"; PermissionsStartOnly = "true";
}; } // nix-bitcoin-services.defaultHardening
// (if cfg.enforceTor
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
);
}; };
users.users.${cfg.user} = { users.users.${cfg.user} = {
name = cfg.user; name = cfg.user;
#uid = config.ids.uids.liquid;
group = cfg.group; group = cfg.group;
extraGroups = [ "keys" ]; extraGroups = [ "keys" ];
description = "Liquid daemon user"; description = "Liquid daemon user";
@ -216,7 +214,6 @@ in {
}; };
users.groups.${cfg.group} = { users.groups.${cfg.group} = {
name = cfg.group; name = cfg.group;
#gid = config.ids.gids.liquid;
}; };
}; };
} }

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.nanopos; cfg = config.services.nanopos;
defaultItemsFile = pkgs.writeText "items.yaml" '' defaultItemsFile = pkgs.writeText "items.yaml" ''
tea: tea:
@ -73,11 +74,9 @@ in {
User = "nanopos"; User = "nanopos";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening
ProtectSystem = "full"; // nix-bitcoin-services.nodejs
NoNewPrivileges = "true"; // nix-bitcoin-services.allowTor;
PrivateDevices = "true";
};
}; };
}; };
} }

View File

@ -0,0 +1,44 @@
{ config, lib, pkgs, ... }:
with lib;
let
defaultHardening = {
PrivateTmp = "true";
ProtectSystem = "full";
ProtectHome = "true";
NoNewPrivileges = "true";
PrivateDevices = "true";
MemoryDenyWriteExecute = "true";
ProtectKernelTunables = "true";
ProtectKernelModules = "true";
ProtectControlGroups = "true";
RestrictAddressFamilies = "AF_UNIX AF_INET AF_INET6";
RestrictNamespaces = "true";
LockPersonality = "true";
IPAddressDeny = "any";
};
in
{
inherit defaultHardening;
# nodejs applications apparently rely on memory write execute
nodejs = { MemoryDenyWriteExecute = "false"; };
# Allow tor traffic. Allow takes precedence over Deny.
allowTor = {
IPAddressAllow = "127.0.0.1/32 ::1/128";
};
# Allow any traffic
allowAnyIP = { IPAddressAllow = "any"; };
enforceTor = mkOption {
type = types.bool;
default = false;
description = ''
"Whether to force Tor on a service by only allowing connections from and
to 127.0.0.1;";
'';
};
}

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.nix-bitcoin-webindex; cfg = config.services.nix-bitcoin-webindex;
indexFile = pkgs.writeText "index.html" '' indexFile = pkgs.writeText "index.html" ''
<html> <html>
@ -43,6 +44,7 @@ in {
If enabled, the webindex service will be installed. If enabled, the webindex service will be installed.
''; '';
}; };
enforceTor = nix-bitcoin-services.enforceTor;
}; };
config = mkIf cfg.enable { config = mkIf cfg.enable {
@ -80,7 +82,11 @@ in {
RemainAfterExit="yes"; RemainAfterExit="yes";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
}; } // nix-bitcoin-services.defaultHardening
// (if cfg.enforceTor
then nix-bitcoin-services.allowTor
else nix-bitcoin-services.allowAnyIP
);
}; };
}; };
} }

View File

@ -60,6 +60,7 @@ in {
services.bitcoind.sysperms = if config.services.electrs.enable then true else null; services.bitcoind.sysperms = if config.services.electrs.enable then true else null;
services.bitcoind.disablewallet = if config.services.electrs.enable then true else null; services.bitcoind.disablewallet = if config.services.electrs.enable then true else null;
services.bitcoind.proxy = config.services.tor.client.socksListenAddress; services.bitcoind.proxy = config.services.tor.client.socksListenAddress;
services.bitcoind.enforceTor = true;
services.bitcoind.port = 8333; services.bitcoind.port = 8333;
services.bitcoind.rpcuser = "bitcoinrpc"; services.bitcoind.rpcuser = "bitcoinrpc";
services.bitcoind.extraConfig = '' services.bitcoind.extraConfig = ''
@ -82,6 +83,7 @@ in {
# clightning # clightning
services.clightning.bitcoin-rpcuser = config.services.bitcoind.rpcuser; services.clightning.bitcoin-rpcuser = config.services.bitcoind.rpcuser;
services.clightning.proxy = config.services.tor.client.socksListenAddress; services.clightning.proxy = config.services.tor.client.socksListenAddress;
services.clightning.enforceTor = true;
services.clightning.always-use-proxy = true; services.clightning.always-use-proxy = true;
services.clightning.bind-addr = "127.0.0.1:9735"; services.clightning.bind-addr = "127.0.0.1:9735";
services.tor.hiddenServices.clightning = { services.tor.hiddenServices.clightning = {
@ -128,6 +130,8 @@ in {
}; };
}; };
services.nix-bitcoin-webindex.enforceTor = true;
services.liquidd.rpcuser = "liquidrpc"; services.liquidd.rpcuser = "liquidrpc";
services.liquidd.prune = 1000; services.liquidd.prune = 1000;
services.liquidd.extraConfig = " services.liquidd.extraConfig = "
@ -136,6 +140,7 @@ in {
"; ";
services.liquidd.listen = true; services.liquidd.listen = true;
services.liquidd.proxy = config.services.tor.client.socksListenAddress; services.liquidd.proxy = config.services.tor.client.socksListenAddress;
services.liquidd.enforceTor = true;
services.liquidd.port = 7042; services.liquidd.port = 7042;
services.tor.hiddenServices.liquidd = { services.tor.hiddenServices.liquidd = {
map = [{ map = [{
@ -143,9 +148,10 @@ in {
}]; }];
version = 3; version = 3;
}; };
services.spark-wallet.onion-service = true; services.spark-wallet.onion-service = true;
services.electrs.port = 50001; services.electrs.port = 50001;
services.electrs.enforceTor = true;
services.electrs.onionport = 50002; services.electrs.onionport = 50002;
services.electrs.nginxport = 50003; services.electrs.nginxport = 50003;
services.electrs.high-memory = false; services.electrs.high-memory = false;

View File

@ -8,6 +8,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.onion-chef; cfg = config.services.onion-chef;
dataDir = "/var/lib/onion-chef/"; dataDir = "/var/lib/onion-chef/";
onion-chef-script = pkgs.writeScript "onion-chef.sh" '' onion-chef-script = pkgs.writeScript "onion-chef.sh" ''
@ -77,11 +78,7 @@ in {
ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}"; ExecStart = "${pkgs.bash}/bin/bash ${onion-chef-script}";
User = "root"; User = "root";
Type = "oneshot"; Type = "oneshot";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening;
ProtectSystem = "full";
NoNewPrivileges = "true";
PrivateDevices = "true";
};
}; };
}; };
} }

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.recurring-donations; cfg = config.services.recurring-donations;
recurring-donations-script = pkgs.writeScript "recurring-donations.sh" '' recurring-donations-script = pkgs.writeScript "recurring-donations.sh" ''
LNCLI="lightning-cli --lightning-dir=${config.services.clightning.dataDir}" LNCLI="lightning-cli --lightning-dir=${config.services.clightning.dataDir}"
@ -88,11 +89,8 @@ in {
# working inside the shell script # working inside the shell script
User = "clightning"; User = "clightning";
Type = "oneshot"; Type = "oneshot";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening
ProtectSystem = "full"; // nix-bitcoin-services.allowTor;
NoNewPrivileges = "true";
PrivateDevices = "true";
};
}; };
systemd.timers.recurring-donations = { systemd.timers.recurring-donations = {
requires = [ "clightning.service" ]; requires = [ "clightning.service" ];

View File

@ -3,6 +3,7 @@
with lib; with lib;
let let
nix-bitcoin-services = pkgs.callPackage ./nix-bitcoin-services.nix { };
cfg = config.services.spark-wallet; cfg = config.services.spark-wallet;
dataDir = "/var/lib/spark-wallet/"; 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 []);
@ -63,11 +64,9 @@ in {
User = "clightning"; User = "clightning";
Restart = "on-failure"; Restart = "on-failure";
RestartSec = "10s"; RestartSec = "10s";
PrivateTmp = "true"; } // nix-bitcoin-services.defaultHardening
ProtectSystem = "full"; // nix-bitcoin-services.nodejs
NoNewPrivileges = "true"; // nix-bitcoin-services.allowTor;
PrivateDevices = "true";
};
}; };
}; };
} }