2
0
mirror of https://github.com/NixOS/nixpkgs.git synced 2025-04-28 19:50:06 +00:00

nixos/bitcoind: change to multi-instance

This commit is contained in:
1000101 2020-07-21 13:43:17 +02:00
parent 55079328c2
commit c6017d9895

View File

@ -3,31 +3,8 @@
with lib; with lib;
let let
cfg = config.services.bitcoind;
pidFile = "${cfg.dataDir}/bitcoind.pid";
configFile = pkgs.writeText "bitcoin.conf" ''
${optionalString cfg.testnet "testnet=1"}
${optionalString (cfg.dbCache != null) "dbcache=${toString cfg.dbCache}"}
${optionalString (cfg.prune != null) "prune=${toString cfg.prune}"}
# Connection options eachBitcoind = config.services.bitcoind;
${optionalString (cfg.port != null) "port=${toString cfg.port}"}
# RPC server options
${optionalString (cfg.rpc.port != null) "rpcport=${toString cfg.rpc.port}"}
${concatMapStringsSep "\n"
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
(attrValues cfg.rpc.users)
}
# Extra config options (from bitcoind nixos service)
${cfg.extraConfig}
'';
cmdlineOptions = escapeShellArgs [
"-conf=${cfg.configFile}"
"-datadir=${cfg.dataDir}"
"-pid=${pidFile}"
];
rpcUserOpts = { name, ... }: { rpcUserOpts = { name, ... }: {
options = { options = {
@ -44,6 +21,9 @@ let
description = '' description = ''
Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the Password HMAC-SHA-256 for JSON-RPC connections. Must be a string of the
format <SALT-HEX>$<HMAC-HEX>. format <SALT-HEX>$<HMAC-HEX>.
Tool (Python script) for HMAC generation is available here:
<link xlink:href="https://github.com/bitcoin/bitcoin/blob/master/share/rpcauth/rpcauth.py"/>
''; '';
}; };
}; };
@ -51,10 +31,10 @@ let
name = mkDefault name; name = mkDefault name;
}; };
}; };
in {
bitcoindOpts = { config, lib, name, ...}: {
options = { options = {
services.bitcoind = {
enable = mkEnableOption "Bitcoin daemon"; enable = mkEnableOption "Bitcoin daemon";
package = mkOption { package = mkOption {
@ -63,12 +43,14 @@ in {
defaultText = "pkgs.bitcoind"; defaultText = "pkgs.bitcoind";
description = "The package providing bitcoin binaries."; description = "The package providing bitcoin binaries.";
}; };
configFile = mkOption { configFile = mkOption {
type = types.path; type = with types; nullOr path;
default = configFile; default = null;
example = "/etc/bitcoind.conf"; example = "/var/lib/${name}/bitcoin.conf";
description = "The configuration file path to supply bitcoind."; description = "The configuration file path to supply bitcoind.";
}; };
extraConfig = mkOption { extraConfig = mkOption {
type = types.lines; type = types.lines;
default = ""; default = "";
@ -79,20 +61,22 @@ in {
''; '';
description = "Additional configurations to be appended to <filename>bitcoin.conf</filename>."; description = "Additional configurations to be appended to <filename>bitcoin.conf</filename>.";
}; };
dataDir = mkOption { dataDir = mkOption {
type = types.path; type = types.path;
default = "/var/lib/bitcoind"; default = "/var/lib/bitcoind-${name}";
description = "The data directory for bitcoind."; description = "The data directory for bitcoind.";
}; };
user = mkOption { user = mkOption {
type = types.str; type = types.str;
default = "bitcoin"; default = "bitcoind-${name}";
description = "The user as which to run bitcoind."; description = "The user as which to run bitcoind.";
}; };
group = mkOption { group = mkOption {
type = types.str; type = types.str;
default = cfg.user; default = config.user;
description = "The group as which to run bitcoind."; description = "The group as which to run bitcoind.";
}; };
@ -110,29 +94,38 @@ in {
bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99"; bob.passwordHMAC = "b2dd077cb54591a2f3139e69a897ac$4e71f08d48b4347cf8eff3815c0e25ae2e9a4340474079f55705f40574f4ec99";
} }
''; '';
type = with types; loaOf (submodule rpcUserOpts); type = with types; attrsOf (submodule rpcUserOpts);
description = '' description = ''
RPC user information for JSON-RPC connnections. RPC user information for JSON-RPC connnections.
''; '';
}; };
}; };
pidFile = mkOption {
type = types.path;
default = "${config.dataDir}/bitcoind.pid";
description = "Location of bitcoind pid file.";
};
testnet = mkOption { testnet = mkOption {
type = types.bool; type = types.bool;
default = false; default = false;
description = "Whether to use the test chain."; description = "Whether to use the testnet instead of mainnet.";
}; };
port = mkOption { port = mkOption {
type = types.nullOr types.port; type = types.nullOr types.port;
default = null; default = null;
description = "Override the default port on which to listen for connections."; description = "Override the default port on which to listen for connections.";
}; };
dbCache = mkOption { dbCache = mkOption {
type = types.nullOr (types.ints.between 4 16384); type = types.nullOr (types.ints.between 4 16384);
default = null; default = null;
example = 4000; example = 4000;
description = "Override the default database cache size in megabytes."; description = "Override the default database cache size in MiB.";
}; };
prune = mkOption { prune = mkOption {
type = types.nullOr (types.coercedTo type = types.nullOr (types.coercedTo
(types.enum [ "disable" "manual" ]) (types.enum [ "disable" "manual" ])
@ -149,26 +142,91 @@ in {
and -rescan. Warning: Reverting this setting requires re-downloading and -rescan. Warning: Reverting this setting requires re-downloading
the entire blockchain. ("disable" = disable pruning blocks, "manual" the entire blockchain. ("disable" = disable pruning blocks, "manual"
= allow manual pruning via RPC, >=550 = automatically prune block files = allow manual pruning via RPC, >=550 = automatically prune block files
to stay under the specified target size in MiB) to stay under the specified target size in MiB).
'';
};
extraCmdlineOptions = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Extra command line options to pass to bitcoind.
Run bitcoind --help to list all available options.
''; '';
}; };
}; };
}; };
in
{
config = mkIf cfg.enable { options = {
environment.systemPackages = [ cfg.package ]; services.bitcoind = mkOption {
systemd.tmpfiles.rules = [ type = types.attrsOf (types.submodule bitcoindOpts);
"d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -" default = {};
"L '${cfg.dataDir}/bitcoin.conf' - - - - '${cfg.configFile}'" description = "Specification of one or more bitcoind instances.";
]; };
systemd.services.bitcoind = { };
config = mkIf (eachBitcoind != {}) {
assertions = flatten (mapAttrsToList (bitcoindName: cfg: [
{
assertion = (cfg.prune != null) -> (cfg.prune == "disable" || cfg.prune == "manual") || (cfg.prune == 0 || cfg.prune == 1) || (cfg.prune >= 550);
message = ''
If set, services.bitcoind.${bitcoindName}.prune has to be "disable", "manual", 0 , 1 or >= 550
'';
}
{
assertion = (cfg.rpc.users != {}) -> (cfg.configFile == null);
message = ''
You cannot set both services.bitcoind.${bitcoindName}.rpc.users and services.bitcoind.${bitcoindName}.configFile
as they are exclusive. RPC user setting would have no effect if custom configFile would be used.
'';
}
]) eachBitcoind);
environment.systemPackages = flatten (mapAttrsToList (bitcoindName: cfg: [
cfg.package
]) eachBitcoind);
systemd.services = mapAttrs' (bitcoindName: cfg: (
nameValuePair "bitcoind-${bitcoindName}" (
let
configFile = pkgs.writeText "bitcoin.conf" ''
# If Testnet is enabled, we need to add [test] section
# otherwise, some options (e.g.: custom RPC port) will not work
${optionalString cfg.testnet "[test]"}
# RPC users
${concatMapStringsSep "\n"
(rpcUser: "rpcauth=${rpcUser.name}:${rpcUser.passwordHMAC}")
(attrValues cfg.rpc.users)
}
# Extra config options (from bitcoind nixos service)
${cfg.extraConfig}
'';
in {
description = "Bitcoin daemon"; description = "Bitcoin daemon";
after = [ "network.target" ]; after = [ "network.target" ];
wantedBy = [ "multi-user.target" ]; wantedBy = [ "multi-user.target" ];
serviceConfig = { serviceConfig = {
User = cfg.user; User = cfg.user;
Group = cfg.group; Group = cfg.group;
ExecStart = "${cfg.package}/bin/bitcoind ${cmdlineOptions}"; ExecStart = ''
${cfg.package}/bin/bitcoind \
${if (cfg.configFile != null) then
"-conf=${cfg.configFile}"
else
"-conf=${configFile}"
} \
-datadir=${cfg.dataDir} \
-pid=${cfg.pidFile} \
${optionalString cfg.testnet "-testnet"}\
${optionalString (cfg.port != null) "-port=${toString cfg.port}"}\
${optionalString (cfg.prune != null) "-prune=${toString cfg.prune}"}\
${optionalString (cfg.dbCache != null) "-dbcache=${toString cfg.dbCache}"}\
${optionalString (cfg.rpc.port != null) "-rpcport=${toString cfg.rpc.port}"}\
${toString cfg.extraCmdlineOptions}
'';
Restart = "on-failure"; Restart = "on-failure";
# Hardening measures # Hardening measures
@ -178,16 +236,26 @@ in {
PrivateDevices = "true"; PrivateDevices = "true";
MemoryDenyWriteExecute = "true"; MemoryDenyWriteExecute = "true";
}; };
}; }
users.users.${cfg.user} = { ))) eachBitcoind;
systemd.tmpfiles.rules = flatten (mapAttrsToList (bitcoindName: cfg: [
"d '${cfg.dataDir}' 0770 '${cfg.user}' '${cfg.group}' - -"
]) eachBitcoind);
users.users = mapAttrs' (bitcoindName: cfg: (
nameValuePair "bitcoind-${bitcoindName}" {
name = cfg.user; name = cfg.user;
group = cfg.group; group = cfg.group;
description = "Bitcoin daemon user"; description = "Bitcoin daemon user";
home = cfg.dataDir; home = cfg.dataDir;
isSystemUser = true; isSystemUser = true;
})) eachBitcoind;
users.groups = mapAttrs' (bitcoindName: cfg: (
nameValuePair "${cfg.group}" { }
)) eachBitcoind;
}; };
users.groups.${cfg.group} = {
name = cfg.group;
};
};
} }