init: dcrd, dcrwallet, and dcrctl at 2.0.6. init modules for dcrd and dcrwallet
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
cfg = config.services.dcrd;
|
||||
settingsFormat = pkgs.formats.ini {
|
||||
listsAsDuplicateKeys = true;
|
||||
};
|
||||
networkPorts = {
|
||||
mainnet = 9108;
|
||||
testnet = 19108;
|
||||
simnet = 18555;
|
||||
regnet = 18655;
|
||||
};
|
||||
effectivePort = if cfg.port == null then networkPorts.${cfg.network} else cfg.port;
|
||||
# dcrd requires settings to be lowercase
|
||||
lowercaseKeys = attrs: lib.mapAttrs' (k: v: { name = lib.strings.toLower k; value = v; }) attrs;
|
||||
baseAppOptions =
|
||||
{}
|
||||
// (if cfg.network == "testnet" then { testnet = true; }
|
||||
else if cfg.network == "simnet" then { simnet = true; }
|
||||
else if cfg.network == "regnet" then { regnet = true; }
|
||||
else {})
|
||||
// lib.optionalAttrs (cfg.port != null || cfg.listenAddress != "0.0.0.0") {
|
||||
listen = "${cfg.listenAddress}:${toString effectivePort}";
|
||||
};
|
||||
combinedAppOptions = lib.recursiveUpdate baseAppOptions (lowercaseKeys cfg.settings);
|
||||
finalSettings = { "Application Options" = combinedAppOptions; };
|
||||
generatedConfig = settingsFormat.generate "dcrd.conf" finalSettings;
|
||||
in
|
||||
{
|
||||
options.services.dcrd = with lib; {
|
||||
enable = mkEnableOption "Decred daemon";
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.dcrd;
|
||||
description = "Package providing the dcrd binary";
|
||||
};
|
||||
network = mkOption {
|
||||
type = types.enum [ "mainnet" "testnet" "simnet" "regnet" ];
|
||||
default = "mainnet";
|
||||
description = "Select which network to run: mainnet, testnet, simnet, or regnet";
|
||||
};
|
||||
port = mkOption {
|
||||
type = types.nullOr types.port;
|
||||
default = null;
|
||||
description = "P2P port override. Null uses the default for the selected network";
|
||||
};
|
||||
listenAddress = mkOption {
|
||||
type = types.str;
|
||||
default = "0.0.0.0";
|
||||
description = "IP address to bind to listen for connections";
|
||||
};
|
||||
openFirewall = mkOption {
|
||||
type = types.bool;
|
||||
default = true;
|
||||
description = "Open P2P port in the firewall";
|
||||
};
|
||||
configFile = mkOption {
|
||||
type = types.nullOr types.path;
|
||||
default = null;
|
||||
description = "Absolute path to a dcrd.conf to use instead of the generated config. When set, the generated settings are ignored.";
|
||||
};
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "dcrd";
|
||||
description = "User to run dcrd as";
|
||||
};
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = cfg.user;
|
||||
description = "Group to run dcrd as";
|
||||
};
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = types.attrsOf settingsFormat.lib.types.atom;
|
||||
};
|
||||
default = {};
|
||||
example = {
|
||||
externalip = [ "203.0.113.42" ];
|
||||
rpclisten = [ "127.0.0.1:9109" "192.168.1.100:9109" ];
|
||||
rpcuser = "dcrd";
|
||||
rpcpass = "hunter2";
|
||||
txindex = true;
|
||||
proxy = "127.0.0.1:9050";
|
||||
};
|
||||
description = ''
|
||||
Options that accept multiple values (like rpclisten, externalip,
|
||||
listen, addpeer, miningaddr, altdnsnames, whitelist) should be
|
||||
specified as lists.
|
||||
|
||||
Single-value options (like rpcuser, rpcpass, proxy, txindex)
|
||||
should be specified as strings or booleans.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
users.groups.${cfg.group} = {};
|
||||
users.users.${cfg.user} = {
|
||||
isSystemUser = true;
|
||||
group = cfg.group;
|
||||
description = "Decred daemon user";
|
||||
};
|
||||
networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ effectivePort ];
|
||||
|
||||
systemd.services.dcrd = {
|
||||
description = "Decred full node";
|
||||
after = [ "network-online.target" ];
|
||||
wants = [ "network-online.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
StateDirectory = "dcrd";
|
||||
StateDirectoryMode = "0750";
|
||||
ExecStart = ''${lib.getExe cfg.package} --appdata=/var/lib/dcrd --configfile=${if cfg.configFile != null then cfg.configFile else generatedConfig}'';
|
||||
Restart = "on-failure";
|
||||
RestartSec = 5;
|
||||
};
|
||||
restartTriggers = [ generatedConfig ] ++ lib.optional (cfg.configFile != null) cfg.configFile;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
{ config, lib, pkgs, ... }:
|
||||
let
|
||||
dcrdEnabled = config.services.dcrd.enable or false;
|
||||
cfg = config.services.dcrwallet;
|
||||
in
|
||||
{
|
||||
options.services.dcrwallet = with lib; {
|
||||
enable = mkEnableOption "Decred wallet daemon";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.dcrwallet;
|
||||
description = "Package providing the dcrwallet binary";
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "dcrwallet";
|
||||
description = "User to run dcrwallet as";
|
||||
};
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = cfg.user;
|
||||
description = "Group to run dcrwallet as";
|
||||
};
|
||||
|
||||
dataDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/dcrwallet";
|
||||
description = "State directory for dcrwallet (appdata)";
|
||||
};
|
||||
|
||||
configFile = mkOption {
|
||||
type = types.path;
|
||||
description = "Path to dcrwallet configuration file (runtime path)";
|
||||
};
|
||||
|
||||
extraPackages = mkOption {
|
||||
type = types.listOf types.package;
|
||||
default = [];
|
||||
example = [ pkgs.dcrctl ];
|
||||
description = "Additional packages to add to the dcrwallet user's environment";
|
||||
};
|
||||
|
||||
operator = {
|
||||
enable = mkEnableOption "Install dcrwallet CLI packages for an operator user";
|
||||
name = mkOption {
|
||||
type = types.str;
|
||||
default = "operator";
|
||||
description = "Operator username to receive dcrwallet package(s) in their environment.";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
users.users.${cfg.user} = {
|
||||
group = cfg.group;
|
||||
home = cfg.dataDir;
|
||||
isNormalUser = true;
|
||||
packages = [ cfg.package ] ++ cfg.extraPackages;
|
||||
# dcrwallet needs read access to the dcrd RPC certificate
|
||||
extraGroups = lib.optional (dcrdEnabled && config.services.dcrd.group != cfg.group) config.services.dcrd.group;
|
||||
};
|
||||
users.groups.${cfg.group} = {};
|
||||
|
||||
systemd.tmpfiles.rules = [
|
||||
"d ${cfg.dataDir} 0750 ${cfg.user} ${cfg.group} -"
|
||||
];
|
||||
|
||||
systemd.services.dcrwallet = {
|
||||
description = "Decred wallet daemon";
|
||||
after = [ "network-online.target" ] ++ lib.optional dcrdEnabled "dcrd.service";
|
||||
wants = [ "network-online.target" ] ++ lib.optional dcrdEnabled "dcrd.service";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
serviceConfig = {
|
||||
User = cfg.user;
|
||||
Group = cfg.group;
|
||||
RuntimeDirectory = "dcrwallet";
|
||||
StateDirectory = "dcrwallet";
|
||||
StateDirectoryMode = "0750";
|
||||
ExecStart = ''${lib.getExe cfg.package} --appdata=${cfg.dataDir} --configfile=${cfg.configFile}'';
|
||||
};
|
||||
restartTriggers = [ cfg.configFile ];
|
||||
};
|
||||
}
|
||||
(lib.mkIf cfg.operator.enable {
|
||||
users.users.${cfg.operator.name} = {
|
||||
packages = [ cfg.package ] ++ cfg.extraPackages;
|
||||
# Add the operator user to the dcrwallet group and dcrd group
|
||||
extraGroups = [ cfg.group ] ++ lib.optional (dcrdEnabled && config.services.dcrd.group != cfg.group) config.services.dcrd.group;
|
||||
};
|
||||
|
||||
# Ensure operator (a member of cfg.group) can read rpc.cert which dcrwallet
|
||||
# writes with mode 0600 by default. We watch for the file and set g+r.
|
||||
systemd.services.dcrwallet-cert-perms = {
|
||||
description = "Ensure group-read on dcrwallet rpc.cert";
|
||||
after = [ "dcrwallet.service" ];
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
};
|
||||
script = ''
|
||||
if [ -e ${cfg.dataDir}/rpc.cert ]; then
|
||||
${pkgs.coreutils}/bin/chmod g+r ${cfg.dataDir}/rpc.cert
|
||||
fi
|
||||
'';
|
||||
};
|
||||
|
||||
systemd.paths.dcrwallet-cert-perms = {
|
||||
description = "Watch dcrwallet rpc.cert to fix permissions";
|
||||
pathConfig = {
|
||||
# PathModified = "${cfg.dataDir}/rpc.cert";
|
||||
PathChanged = "${cfg.dataDir}/rpc.cert";
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
})
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user