diff --git a/nixos/modules/misc/ids.nix b/nixos/modules/misc/ids.nix index feecee3225be7..b09f791aaa722 100644 --- a/nixos/modules/misc/ids.nix +++ b/nixos/modules/misc/ids.nix @@ -289,6 +289,10 @@ rpc = 271; geoip = 272; fcron = 273; + bitcoin = 275; + dash = 276; + parity = 277; + monero = 278; # When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399! @@ -547,6 +551,10 @@ #rpc = 271; # unused #geoip = 272; # unused fcron = 273; + bitcoin = 275; + dash = 276; + parity = 277; + monero = 278; # When adding a gid, make sure it doesn't match an existing # uid. Users and groups with the same name should have equal diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 0035368273ca1..9d58dac2666f5 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -140,6 +140,10 @@ ./services/backup/rsnapshot.nix ./services/backup/tarsnap.nix ./services/backup/znapzend.nix + ./services/blockchain/bitcoin.nix + ./services/blockchain/dash.nix + ./services/blockchain/parity.nix + ./services/blockchain/monero.nix ./services/cluster/fleet.nix ./services/cluster/kubernetes.nix ./services/cluster/panamax.nix diff --git a/nixos/modules/services/blockchain/bitcoin-core-generic.nix b/nixos/modules/services/blockchain/bitcoin-core-generic.nix new file mode 100644 index 0000000000000..785d408e91dc7 --- /dev/null +++ b/nixos/modules/services/blockchain/bitcoin-core-generic.nix @@ -0,0 +1,123 @@ +{ config, options, pkgs, lib +, name +, description +, defaultUser +, userDescription +, defaultGroup +, groupDescription +, defaultPackage +, daemonExec +, cliExec +, ... }: + +with lib; + +let + + stateDir = "/var/lib/${name}"; + pidFile = "${stateDir}/${name}.pid"; + + cu = pkgs.coreutils; + + cfg = config.services.${name}; + opts = options.services.${name}; + + configFile = optionalString (!isNull cfg.extraConfig) (" -conf=${pkgs.writeText "${name}.conf" cfg.extraConfig}"); + +in + +{ + + options = { + + services.${name} = { + + enable = mkOption { + default = false; + description = "Enable ${description}"; + type = types.bool; + }; + + package = mkOption { + default = defaultPackage; + description = "Package to use"; + type = types.package; + }; + + user = mkOption { + default = defaultUser; + description = userDescription; + type = types.str; + }; + + group = mkOption { + default = defaultGroup; + description = groupDescription; + type = types.str; + }; + + dataDir = mkOption { + description = "Data directory"; + type = types.path; + }; + + extraConfig = mkOption { + description = "Additional configuration"; + type = types.nullOr types.str; + }; + + }; + + }; + + config = mkIf cfg.enable { + + systemd.services.${name} = { + + inherit description; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.dataDir; + + Type = "forking"; + PIDFile = pidFile; + + ExecStartPre = [ + "+${cu}/bin/mkdir -p ${stateDir}" + "+${cu}/bin/chown -R ${cfg.user}:${cfg.group} ${stateDir}" + "+${cu}/bin/chmod -R 700 ${stateDir}" + ]; + ExecStart = "${cfg.package}/bin/${daemonExec} -daemon -pid=${pidFile} -datadir=${cfg.dataDir}${configFile}"; + ExecStop = "${cfg.package}/bin/${cliExec} -rpcwait -datadir=${cfg.dataDir}${configFile} stop"; + + Restart = "always"; + PrivateTmp = true; + TimeoutStopSec = 60; + }; + }; + + users = { + extraUsers = optionalAttrs (cfg.user == opts.user.default) (singleton { + name = opts.user.default; + uid = config.ids.uids.${opts.user.default}; + group = opts.group.default; + description = opts.user.description; + home = cfg.dataDir; + createHome = true; + }); + + extraGroups = optionalAttrs (cfg.group == opts.group.default) (singleton { + name = opts.group.default; + gid = config.ids.gids.${opts.group.default}; + }); + }; + + environment.systemPackages = [ cfg.package ]; + + }; + +} diff --git a/nixos/modules/services/blockchain/bitcoin.nix b/nixos/modules/services/blockchain/bitcoin.nix new file mode 100644 index 0000000000000..7cc89d6b65ff4 --- /dev/null +++ b/nixos/modules/services/blockchain/bitcoin.nix @@ -0,0 +1,16 @@ +{ config, options, pkgs, lib, ... }: + +(import ./bitcoin-core-generic.nix { + inherit config options pkgs lib; + + name = "bitcoind"; + description = "Bitcoin's distributed currency daemon"; + defaultUser = "bitcoin"; + userDescription = "Bitcoin daemon user"; + defaultGroup = "bitcoin"; + groupDescription = "Bitcoin daemon group"; + defaultPackage = pkgs.altcoins.bitcoind; + daemonExec = "bitcoind"; + cliExec = "bitcoin-cli"; + +}) diff --git a/nixos/modules/services/blockchain/dash.nix b/nixos/modules/services/blockchain/dash.nix new file mode 100644 index 0000000000000..762bf8619d205 --- /dev/null +++ b/nixos/modules/services/blockchain/dash.nix @@ -0,0 +1,16 @@ +{ config, options, pkgs, lib, ... }: + +(import ./bitcoin-core-generic.nix { + inherit config options pkgs lib; + + name = "dashd"; + description = "Dash's distributed currency daemon"; + defaultUser = "dash"; + userDescription = "Dash daemon user"; + defaultGroup = "dash"; + groupDescription = "Dash daemon group"; + defaultPackage = pkgs.altcoins.dashpay; + daemonExec = "dashd"; + cliExec = "dash-cli"; + +}) diff --git a/nixos/modules/services/blockchain/monero.nix b/nixos/modules/services/blockchain/monero.nix new file mode 100644 index 0000000000000..599b484be7487 --- /dev/null +++ b/nixos/modules/services/blockchain/monero.nix @@ -0,0 +1,234 @@ +{ config, options, pkgs, lib, ... }: + +with lib; + +let + + description = "Monero Full Node"; + walletDescription = "Monero Wallet RPC Daemon"; + + cu = pkgs.coreutils; + + cfg = config.services.monerod; + opts = options.services.monerod; + + configFile = optionalString (!isNull cfg.extraConfig) (" --config-file=${pkgs.writeText "monerod.conf" cfg.extraConfig}"); + + makeWalletJob = name: conf: + let + + options = [ + "--wallet-file=${conf.walletDir}/${conf.walletFile}" + (optionalString (!isNull conf.walletPassword) "--password=${conf.walletPassword}") + (optionalString (!isNull conf.walletPasswordFile) "--password-file=${conf.walletPasswordFile}") + (optionalString (!isNull conf.bindAddress) "--rpc-bind-ip=${conf.bindAddress}") + (optionalString (conf.confirmExternalBind) "--confirm-external-bind") + (optionalString (!isNull conf.bindPort) "--rpc-bind-port=${toString conf.bindPort}") + (optionalString (!isNull conf.login) "--rpc-login=${conf.login}") + ] ++ conf.extraOptions; + + in + + { + + inherit (conf) enable; + + description = "${walletDescription}: ${name}"; + after = [ "network.target" ]; + wantedBy = [ (if conf.independent then "multi-user.target" else "monerod.service") ]; + bindsTo = [ (optionalString (!conf.independent) "monerod.service") ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.dataDir; + + ExecStartPre = [ + "+${cu}/bin/chown -R ${cfg.user}:${cfg.group} ${conf.walletDir}" + "+${cu}/bin/chmod -R 700 ${conf.walletDir}" + ]; + ExecStart = "${cfg.package}/bin/monero-wallet-rpc ${toString options}"; + + Restart = "always"; + PrivateTmp = true; + TimeoutStopSec = 60; + }; + + }; + +in + +{ + + options = { + + services.monerod = { + + enable = mkOption { + default = false; + description = "Enable ${description}"; + type = types.bool; + }; + + package = mkOption { + default = pkgs.monero; + description = "Package to use"; + type = types.package; + }; + + user = mkOption { + default = "monero"; + description = "Monero daemon user"; + type = types.str; + }; + + group = mkOption { + default = "monero"; + description = "Monero daemon group"; + type = types.str; + }; + + dataDir = mkOption { + description = "Data directory"; + type = types.path; + }; + + extraConfig = mkOption { + description = "Additional configuration"; + type = types.nullOr types.str; + }; + + wallets = mkOption { + default = {}; + description = "Additional wallet RPC daemons to start"; + type = with types; attrsOf (submodule { + + options = { + + enable = mkOption { + default = true; + description = "Enable ${walletDescription}"; + type = types.bool; + }; + + independent = mkOption { + default = false; + description = "Wallet daemon is independent from monerod"; + type = types.bool; + }; + + walletDir = mkOption { + description = "Directory of wallet files"; + type = types.path; + }; + + walletFile = mkOption { + default = "wallet.bin"; + description = "Wallet file name"; + type = types.str; + }; + + walletPassword = mkOption { + default = null; + description = "Wallet password"; + type = types.nullOr types.str; + }; + + walletPasswordFile = mkOption { + default = null; + description = "Wallet password file"; + type = types.nullOr types.path; + }; + + bindAddress = mkOption { + default = null; + description = "Specify ip to bind RPC server"; + type = types.nullOr types.str; + }; + + bindPort = mkOption { + default = null; + description = "Sets bind port for RPC server"; + type = types.nullOr types.int; + }; + + confirmExternalBind = mkOption { + default = false; + description = "Confirm bindAddress value is NOT a loopback (local) IP"; + type = types.bool; + }; + + login = mkOption { + default = null; + description = "Specify username[:password] required for RPC server"; + type = types.nullOr types.str; + }; + + extraOptions = mkOption { + default = []; + description = "Additional command-line options"; + type = types.listOf types.str; + }; + + }; + + }); + + + }; + + }; + + }; + + config = mkIf cfg.enable (mkMerge [{ + + systemd.services.monerod = { + + inherit description; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.dataDir; + + Type = "forking"; + GuessMainPID = "no"; + + ExecStart = "${cfg.package}/bin/monerod --detach${configFile} --data-dir=${cfg.dataDir}"; + + Restart = "always"; + PrivateTmp = true; + TimeoutStopSec = 60; + }; + }; + + users = { + extraUsers = optionalAttrs (cfg.user == opts.user.default) (singleton { + name = opts.user.default; + uid = config.ids.uids.${opts.user.default}; + group = opts.group.default; + description = opts.user.description; + home = cfg.dataDir; + createHome = true; + }); + + extraGroups = optionalAttrs (cfg.group == opts.group.default) (singleton { + name = opts.group.default; + gid = config.ids.gids.${opts.group.default}; + }); + }; + + + environment.systemPackages = [ cfg.package ]; + + + } (mkIf (cfg.wallets != {}) { + + systemd.services = mapAttrs' (name: value: nameValuePair "monero-wallet-${name}" (makeWalletJob name value)) cfg.wallets; + + })]); + +} diff --git a/nixos/modules/services/blockchain/parity.nix b/nixos/modules/services/blockchain/parity.nix new file mode 100644 index 0000000000000..49bee31252915 --- /dev/null +++ b/nixos/modules/services/blockchain/parity.nix @@ -0,0 +1,103 @@ +{ config, options, pkgs, lib, ... }: + +with lib; + +let + + description = "Parity Daemon"; + + cu = pkgs.coreutils; + + cfg = config.services.parity; + opts = options.services.parity; + + configFile = optionalString (!isNull cfg.extraConfig) (" --config=${pkgs.writeText "config.toml" cfg.extraConfig}"); + +in + +{ + + options = { + + services.parity = { + + enable = mkOption { + default = false; + description = "Enable ${description}"; + type = types.bool; + }; + + package = mkOption { + default = pkgs.altcoins.parity; + description = "Package to use"; + type = types.package; + }; + + user = mkOption { + default = "parity"; + description = "Parity daemon user"; + type = types.str; + }; + + group = mkOption { + default = "parity"; + description = "Parity daemon group"; + type = types.str; + }; + + dataDir = mkOption { + description = "Data directory"; + type = types.path; + }; + + extraConfig = mkOption { + description = "Additional configuration"; + type = types.nullOr types.str; + }; + + }; + + }; + + config = mkIf cfg.enable { + + systemd.services.parity = { + + inherit description; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + User = cfg.user; + Group = cfg.group; + WorkingDirectory = cfg.dataDir; + + ExecStart = "${cfg.package}/bin/parity${configFile} --base-path=${cfg.dataDir}"; + + Restart = "always"; + PrivateTmp = true; + TimeoutStopSec = 60; + }; + }; + + users = { + extraUsers = optionalAttrs (cfg.user == opts.user.default) (singleton { + name = opts.user.default; + uid = config.ids.uids.${opts.user.default}; + group = opts.group.default; + description = opts.user.description; + home = cfg.dataDir; + createHome = true; + }); + + extraGroups = optionalAttrs (cfg.group == opts.group.default) (singleton { + name = opts.group.default; + gid = config.ids.gids.${opts.group.default}; + }); + }; + + environment.systemPackages = [ cfg.package ]; + + }; + +}