diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index 90825aa3401..cadcb2e99dc 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -321,6 +321,7 @@ ./services/backup/znapzend.nix ./services/blockchain/ethereum/geth.nix ./services/blockchain/ethereum/erigon.nix + ./services/blockchain/ethereum/lighthouse.nix ./services/backup/zrepl.nix ./services/cluster/corosync/default.nix ./services/cluster/hadoop/default.nix diff --git a/nixos/modules/services/blockchain/ethereum/erigon.nix b/nixos/modules/services/blockchain/ethereum/erigon.nix index 16277473097..892262d246b 100644 --- a/nixos/modules/services/blockchain/ethereum/erigon.nix +++ b/nixos/modules/services/blockchain/ethereum/erigon.nix @@ -13,6 +13,15 @@ in { services.erigon = { enable = mkEnableOption (lib.mdDoc "Ethereum implementation on the efficiency frontier"); + group = mkOption { + type = types.str; + default = "ethereum"; + description = lib.mdDoc '' + Group of the user running the lighthouse process. This is used to share the jwt + secret with the execution layer. + ''; + }; + settings = mkOption { description = lib.mdDoc '' Configuration for Erigon @@ -55,6 +64,19 @@ in { }; config = mkIf cfg.enable { + users = { + users.erigon = { + name = "erigon"; + group = cfg.group; + description = "Erigon user"; + home = "/var/lib/erigon"; + isSystemUser = true; + }; + groups = mkIf (cfg.group == "ethereum") { + ethereum = {}; + }; + }; + # Default values are the same as in the binary, they are just written here for convenience. services.erigon.settings = { datadir = mkDefault "/var/lib/erigon"; @@ -77,10 +99,11 @@ in { serviceConfig = { ExecStart = "${pkgs.erigon}/bin/erigon --config ${configFile}"; + User = "erigon"; + Group = cfg.group; Restart = "on-failure"; StateDirectory = "erigon"; CapabilityBoundingSet = ""; - DynamicUser = true; NoNewPrivileges = true; PrivateTmp = true; ProtectHome = true; @@ -97,7 +120,6 @@ in { RestrictNamespaces = true; LockPersonality = true; RemoveIPC = true; - RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; SystemCallFilter = [ "@system-service" "~@privileged" ]; }; }; diff --git a/nixos/modules/services/blockchain/ethereum/lighthouse.nix b/nixos/modules/services/blockchain/ethereum/lighthouse.nix new file mode 100644 index 00000000000..57e32132f1d --- /dev/null +++ b/nixos/modules/services/blockchain/ethereum/lighthouse.nix @@ -0,0 +1,333 @@ +{ config, lib, pkgs, ... }: + +with lib; +let + + cfg = config.services.lighthouse; +in { + + options = { + services.lighthouse = { + beacon = mkOption { + description = lib.mdDoc "Beacon node"; + type = types.submodule { + options = { + enable = lib.mkEnableOption (lib.mdDoc "Lightouse Beacon node"); + + dataDir = mkOption { + type = types.str; + default = "/var/lib/lighthouse-beacon"; + description = lib.mdDoc '' + Directory where data will be stored. Each chain will be stored under it's own specific subdirectory. + ''; + }; + + address = mkOption { + type = types.str; + default = "0.0.0.0"; + description = lib.mdDoc '' + Listen address of Beacon node. + ''; + }; + + port = mkOption { + type = types.port; + default = 9000; + description = lib.mdDoc '' + Port number the Beacon node will be listening on. + ''; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Open the port in the firewall + ''; + }; + + disableDepositContractSync = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc '' + Explictly disables syncing of deposit logs from the execution node. + This overrides any previous option that depends on it. + Useful if you intend to run a non-validating beacon node. + ''; + }; + + group = mkOption { + type = types.str; + default = "ethereum"; + description = lib.mdDoc '' + Group of the user running the lighthouse process. This is used to share the jwt + secret with the execution layer. + ''; + }; + + execution = { + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Listen address for the execution layer. + ''; + }; + + port = mkOption { + type = types.port; + default = 8551; + description = lib.mdDoc '' + Port number the Beacon node will be listening on for the execution layer. + ''; + }; + + jwtPath = mkOption { + type = types.str; + default = ""; + description = lib.mdDoc '' + Path for the jwt secret required to connect to the execution layer. + ''; + }; + }; + + http = { + enable = lib.mkEnableOption (lib.mdDoc "Beacon node http api"); + port = mkOption { + type = types.port; + default = 5052; + description = lib.mdDoc '' + Port number of Beacon node RPC service. + ''; + }; + + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Listen address of Beacon node RPC service. + ''; + }; + }; + + metrics = { + enable = lib.mkEnableOption (lib.mdDoc "Beacon node prometheus metrics"); + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Listen address of Beacon node metrics service. + ''; + }; + + port = mkOption { + type = types.port; + default = 5054; + description = lib.mdDoc '' + Port number of Beacon node metrics service. + ''; + }; + }; + + extraArgs = mkOption { + type = types.str; + description = lib.mdDoc '' + Additional arguments passed to the lighthouse beacon command. + ''; + default = ""; + example = ""; + }; + }; + }; + }; + + validator = mkOption { + description = lib.mdDoc "Validator node"; + type = types.submodule { + options = { + enable = mkOption { + type = types.bool; + default = false; + description = lib.mdDoc "Enable Lightouse Validator node."; + }; + + dataDir = mkOption { + type = types.str; + default = "/var/lib/lighthouse-validator"; + description = lib.mdDoc '' + Directory where data will be stored. Each chain will be stored under it's own specific subdirectory. + ''; + }; + + beaconNodes = mkOption { + type = types.listOf types.str; + default = ["http://localhost:5052"]; + description = lib.mdDoc '' + Beacon nodes to connect to. + ''; + }; + + metrics = { + enable = lib.mkEnableOption (lib.mdDoc "Validator node prometheus metrics"); + address = mkOption { + type = types.str; + default = "127.0.0.1"; + description = lib.mdDoc '' + Listen address of Validator node metrics service. + ''; + }; + + port = mkOption { + type = types.port; + default = 5056; + description = lib.mdDoc '' + Port number of Validator node metrics service. + ''; + }; + }; + + extraArgs = mkOption { + type = types.str; + description = lib.mdDoc '' + Additional arguments passed to the lighthouse validator command. + ''; + default = ""; + example = ""; + }; + }; + }; + }; + + network = mkOption { + type = types.enum [ "mainnet" "prater" "goerli" "gnosis" "kiln" "ropsten" "sepolia" ]; + default = "mainnet"; + description = lib.mdDoc '' + The network to connect to. Mainnet is the default ethereum network. + ''; + }; + + extraArgs = mkOption { + type = types.str; + description = lib.mdDoc '' + Additional arguments passed to every lighthouse command. + ''; + default = ""; + example = ""; + }; + }; + }; + + config = mkIf (cfg.beacon.enable || cfg.validator.enable) { + + users = { + users.lighthouse-beacon = { + name = "lighthouse-beacon"; + group = cfg.beacon.group; + description = "Lighthouse beacon node user"; + home = "${cfg.beacon.dataDir}"; + isSystemUser = true; + }; + groups = mkIf (cfg.beacon.group == "ethereum") { + ethereum = {}; + }; + }; + + environment.systemPackages = [ pkgs.lighthouse ] ; + + networking.firewall = mkIf cfg.beacon.enable { + allowedTCPPorts = mkIf cfg.beacon.openFirewall [ cfg.beacon.port ]; + allowedUDPPorts = mkIf cfg.beacon.openFirewall [ cfg.beacon.port ]; + }; + + + systemd.services.lighthouse-beacon = mkIf cfg.beacon.enable { + description = "Lighthouse beacon node (connect to P2P nodes and verify blocks)"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + script = '' + # make sure the chain data directory is created on first run + mkdir -p ${cfg.beacon.dataDir}/${cfg.network} + + ${pkgs.lighthouse}/bin/lighthouse beacon_node \ + --disable-upnp \ + ${lib.optionalString cfg.beacon.disableDepositContractSync "--disable-deposit-contract-sync"} \ + --port ${toString cfg.beacon.port} \ + --listen-address ${cfg.beacon.address} \ + --network ${cfg.network} \ + --datadir ${cfg.beacon.dataDir}/${cfg.network} \ + --execution-endpoint http://${cfg.beacon.execution.address}:${toString cfg.beacon.execution.port} \ + --execution-jwt ${cfg.beacon.execution.jwtPath} \ + ${lib.optionalString cfg.beacon.http.enable '' --http --http-address ${cfg.beacon.http.address} --http-port ${toString cfg.beacon.http.port}''} \ + ${lib.optionalString cfg.beacon.metrics.enable '' --metrics --metrics-address ${cfg.beacon.metrics.address} --metrics-port ${toString cfg.beacon.metrics.port}''} \ + ${cfg.extraArgs} ${cfg.beacon.extraArgs} + ''; + serviceConfig = { + User = "lighthouse-beacon"; + Group = cfg.beacon.group; + Restart = "on-failure"; + StateDirectory = "lighthouse-beacon"; + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = true; + ProtectClock = true; + ProtectProc = "noaccess"; + ProcSubset = "pid"; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectHostname = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + RemoveIPC = true; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + }; + + systemd.services.lighthouse-validator = mkIf cfg.validator.enable { + description = "Lighthouse validtor node (manages validators, using data obtained from the beacon node via a HTTP API)"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + script = '' + # make sure the chain data directory is created on first run + mkdir -p ${cfg.validator.dataDir}/${cfg.network} + + ${pkgs.lighthouse}/bin/lighthouse validator_client \ + --network ${cfg.network} \ + --beacon-nodes ${lib.concatStringsSep "," cfg.validator.beaconNodes} \ + --datadir ${cfg.validator.dataDir}/${cfg.network} + ${optionalString cfg.validator.metrics.enable ''--metrics --metrics-address ${cfg.validator.metrics.address} --metrics-port ${toString cfg.validator.metrics.port}''} \ + ${cfg.extraArgs} ${cfg.validator.extraArgs} + ''; + + serviceConfig = { + Restart = "on-failure"; + StateDirectory = "lighthouse-validator"; + CapabilityBoundingSet = ""; + DynamicUser = true; + NoNewPrivileges = true; + PrivateTmp = true; + ProtectHome = true; + ProtectClock = true; + ProtectProc = "noaccess"; + ProcSubset = "pid"; + ProtectKernelLogs = true; + ProtectKernelModules = true; + ProtectKernelTunables = true; + ProtectControlGroups = true; + ProtectHostname = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + RemoveIPC = true; + RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ]; + SystemCallFilter = [ "@system-service" "~@privileged" ]; + }; + }; + }; +}