Loading nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -692,6 +692,7 @@ ./services/hardware/nvidia-optimus.nix ./services/hardware/openrgb.nix ./services/hardware/pcscd.nix ./services/hardware/pdudaemon.nix ./services/hardware/pid-fan-controller.nix ./services/hardware/pommed.nix ./services/hardware/power-profiles-daemon.nix Loading nixos/modules/services/hardware/pdudaemon.nix 0 → 100644 +146 −0 Original line number Diff line number Diff line { config, pkgs, lib, ... }: let cfg = config.services.pdudaemon; configFile = pkgs.writeText "pdudaemon.conf" ( lib.generators.toJSON { } { daemon = { hostname = cfg.bindAddress; port = cfg.port; logging_level = cfg.logLevel; listener = cfg.listener; }; pdus = cfg.pdus; } ); in { meta = { maintainers = with lib.maintainers; [ aiyion emantor ]; }; options = { services.pdudaemon = { enable = lib.mkEnableOption "PDUDaemon"; package = lib.mkPackageOption pkgs "pdudaemon" { }; bindAddress = lib.mkOption { default = "0.0.0.0"; type = lib.types.str; description = "Bind address for the PDUDaemon."; }; port = lib.mkOption { default = 16421; type = lib.types.port; description = "Port to bind to."; }; openFirewall = lib.mkOption { default = false; type = lib.types.bool; description = '' Whether to automatically open the PDUDaemon listen port in the firewall. ''; }; listener = lib.mkOption { default = "http"; type = lib.types.enum [ "http" "tcp" ]; description = "Which kind of listener to provide."; }; logLevel = lib.mkOption { default = "error"; type = lib.types.enum [ "debug" "info" "warning" "error" ]; description = "PDUDaemon log level."; }; pdus = lib.mkOption { type = with lib.types; attrsOf anything; default = { }; description = '' Structural pdus section of PDUDaemon's pdudaemon.conf. Refer to <https://github.com/pdudaemon/pdudaemon/blob/main/share/pdudaemon.conf> for more examples. ''; example = lib.literalExpression '' { cbs350-poe-switch = { driver = "snmpv1"; community = "private"; oid = ".1.3.6.1.2.1.105.1.1.1.3.1.*; onsetting = 1; offsetting = 2; }; energenie = { driver = "EG-PMS"; device = "aa:bb:cc:xx:yy"; }; local = { driver = "localcmdline"; }; }; ''; }; }; }; config = lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ]; systemd.services.pdudaemon = { after = [ "network-online.target" ]; description = "Control and Queueing daemon for PDUs"; serviceConfig = { ExecStart = "${lib.getExe cfg.package} --conf ${configFile}"; Type = "simple"; DynamicUser = "yes"; StateDirectory = "pdudaemon"; ProtectHome = true; Restart = "on-failure"; CapabilityBoundingSet = ""; PrivateDevices = true; ProtectClock = true; ProtectKernelLogs = true; ProtectControlGroups = true; ProtectKernelModules = true; SystemCallArchitectures = "native"; MemoryDenyWriteExecute = true; RestrictNamespaces = true; ProtectHostname = true; LockPersonality = true; ProtectKernelTunables = true; RestrictRealtime = true; ProtectProc = "invisible"; ProcSubset = "pid"; PrivateUsers = true; SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; }; wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; }; }; } nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -1252,6 +1252,7 @@ in inherit runTest; }; pdns-recursor = runTest ./pdns-recursor.nix; pdudaemon = runTest ./pdudaemon.nix; peerflix = runTest ./peerflix.nix; peering-manager = runTest ./web-apps/peering-manager.nix; peertube = handleTestOn [ "x86_64-linux" ] ./web-apps/peertube.nix { }; Loading nixos/tests/pdudaemon.nix 0 → 100644 +50 −0 Original line number Diff line number Diff line { pkgs, ... }: { name = "PDUDaemon"; meta.maintainers = with pkgs.lib.maintainers; [ aiyion emantor ]; nodes.pdudaemonhost = { pkgs, ... }: { environment.systemPackages = [ pkgs.curl ]; services.pdudaemon.enable = true; services.pdudaemon.openFirewall = true; services.pdudaemon.pdus = { testpduhost = { driver = "localcmdline"; cmd_on = "echo '%s on' >> /tmp/pdu"; cmd_off = "echo '%s off' >> /tmp/pdu"; }; }; }; nodes.clienthost = { pkgs, ... }: { environment.systemPackages = [ pkgs.curl ]; }; testScript = { nodes, ... }: #python '' with subtest("Wait for pdudaemon startup"): pdudaemonhost.start() pdudaemonhost.wait_for_unit("pdudaemon.service") pdudaemonhost.wait_for_open_port(16421) print(pdudaemonhost.succeed("curl 'http://localhost:16421/power/control/on?hostname=testpduhost&port=1'")) with subtest("Connect from client"): clienthost.start() clienthost.wait_until_succeeds("curl 'http://pdudaemonhost:16421/power/control/off?hostname=testpduhost&port=1'") with subtest("Check systemd hardening does not degrade unnoticed"): exact_threshold = 15 service_name = "pdudaemon" pdudaemonhost.fail(f"systemd-analyze security {service_name}.service --threshold={exact_threshold-1}") pdudaemonhost.succeed(f"systemd-analyze security {service_name}.service --threshold={exact_threshold}") ''; } pkgs/by-name/pd/pdudaemon/package.nix 0 → 100644 +62 −0 Original line number Diff line number Diff line { lib, fetchFromGitHub, python3Packages, nixosTests, }: python3Packages.buildPythonApplication (finalAttrs: { pname = "pdudaemon"; version = "1.1.1"; pyproject = true; src = fetchFromGitHub { owner = "pdudaemon"; repo = "pdudaemon"; tag = finalAttrs.version; hash = "sha256-YjM1RmsdRfNyxCzK+PmSH8n7ZJ3qeIskTPxu2+EaupQ="; }; build-system = with python3Packages; [ setuptools setuptools-scm ]; dependencies = with python3Packages; [ aiohttp requests pexpect systemd-python paramiko pyserial hidapi pysnmp pyasn1 pyusb pymodbus ]; nativeCheckInputs = with python3Packages; [ pytest-asyncio pytest-mock pytestCheckHook ]; __structuredAttrs = true; passthru.tests = { inherit (nixosTests) pdudaemon; }; meta = { changelog = "https://github.com/pdudaemon/pdudaemon/releases/tag/${finalAttrs.src.tag}"; description = "Python Daemon for controlling/sequentially executing commands to PDUs (Power Distribution Units)"; homepage = "https://github.com/pdudaemon/pdudaemon"; license = lib.licenses.gpl2Plus; maintainers = with lib.maintainers; [ aiyion emantor ]; mainProgram = "pdudaemon"; }; }) Loading
nixos/modules/module-list.nix +1 −0 Original line number Diff line number Diff line Loading @@ -692,6 +692,7 @@ ./services/hardware/nvidia-optimus.nix ./services/hardware/openrgb.nix ./services/hardware/pcscd.nix ./services/hardware/pdudaemon.nix ./services/hardware/pid-fan-controller.nix ./services/hardware/pommed.nix ./services/hardware/power-profiles-daemon.nix Loading
nixos/modules/services/hardware/pdudaemon.nix 0 → 100644 +146 −0 Original line number Diff line number Diff line { config, pkgs, lib, ... }: let cfg = config.services.pdudaemon; configFile = pkgs.writeText "pdudaemon.conf" ( lib.generators.toJSON { } { daemon = { hostname = cfg.bindAddress; port = cfg.port; logging_level = cfg.logLevel; listener = cfg.listener; }; pdus = cfg.pdus; } ); in { meta = { maintainers = with lib.maintainers; [ aiyion emantor ]; }; options = { services.pdudaemon = { enable = lib.mkEnableOption "PDUDaemon"; package = lib.mkPackageOption pkgs "pdudaemon" { }; bindAddress = lib.mkOption { default = "0.0.0.0"; type = lib.types.str; description = "Bind address for the PDUDaemon."; }; port = lib.mkOption { default = 16421; type = lib.types.port; description = "Port to bind to."; }; openFirewall = lib.mkOption { default = false; type = lib.types.bool; description = '' Whether to automatically open the PDUDaemon listen port in the firewall. ''; }; listener = lib.mkOption { default = "http"; type = lib.types.enum [ "http" "tcp" ]; description = "Which kind of listener to provide."; }; logLevel = lib.mkOption { default = "error"; type = lib.types.enum [ "debug" "info" "warning" "error" ]; description = "PDUDaemon log level."; }; pdus = lib.mkOption { type = with lib.types; attrsOf anything; default = { }; description = '' Structural pdus section of PDUDaemon's pdudaemon.conf. Refer to <https://github.com/pdudaemon/pdudaemon/blob/main/share/pdudaemon.conf> for more examples. ''; example = lib.literalExpression '' { cbs350-poe-switch = { driver = "snmpv1"; community = "private"; oid = ".1.3.6.1.2.1.105.1.1.1.3.1.*; onsetting = 1; offsetting = 2; }; energenie = { driver = "EG-PMS"; device = "aa:bb:cc:xx:yy"; }; local = { driver = "localcmdline"; }; }; ''; }; }; }; config = lib.mkIf cfg.enable { networking.firewall.allowedTCPPorts = lib.mkIf cfg.openFirewall [ cfg.port ]; systemd.services.pdudaemon = { after = [ "network-online.target" ]; description = "Control and Queueing daemon for PDUs"; serviceConfig = { ExecStart = "${lib.getExe cfg.package} --conf ${configFile}"; Type = "simple"; DynamicUser = "yes"; StateDirectory = "pdudaemon"; ProtectHome = true; Restart = "on-failure"; CapabilityBoundingSet = ""; PrivateDevices = true; ProtectClock = true; ProtectKernelLogs = true; ProtectControlGroups = true; ProtectKernelModules = true; SystemCallArchitectures = "native"; MemoryDenyWriteExecute = true; RestrictNamespaces = true; ProtectHostname = true; LockPersonality = true; ProtectKernelTunables = true; RestrictRealtime = true; ProtectProc = "invisible"; ProcSubset = "pid"; PrivateUsers = true; SystemCallFilter = [ "@system-service" "~@privileged" "~@resources" ]; }; wantedBy = [ "multi-user.target" ]; wants = [ "network-online.target" ]; }; }; }
nixos/tests/all-tests.nix +1 −0 Original line number Diff line number Diff line Loading @@ -1252,6 +1252,7 @@ in inherit runTest; }; pdns-recursor = runTest ./pdns-recursor.nix; pdudaemon = runTest ./pdudaemon.nix; peerflix = runTest ./peerflix.nix; peering-manager = runTest ./web-apps/peering-manager.nix; peertube = handleTestOn [ "x86_64-linux" ] ./web-apps/peertube.nix { }; Loading
nixos/tests/pdudaemon.nix 0 → 100644 +50 −0 Original line number Diff line number Diff line { pkgs, ... }: { name = "PDUDaemon"; meta.maintainers = with pkgs.lib.maintainers; [ aiyion emantor ]; nodes.pdudaemonhost = { pkgs, ... }: { environment.systemPackages = [ pkgs.curl ]; services.pdudaemon.enable = true; services.pdudaemon.openFirewall = true; services.pdudaemon.pdus = { testpduhost = { driver = "localcmdline"; cmd_on = "echo '%s on' >> /tmp/pdu"; cmd_off = "echo '%s off' >> /tmp/pdu"; }; }; }; nodes.clienthost = { pkgs, ... }: { environment.systemPackages = [ pkgs.curl ]; }; testScript = { nodes, ... }: #python '' with subtest("Wait for pdudaemon startup"): pdudaemonhost.start() pdudaemonhost.wait_for_unit("pdudaemon.service") pdudaemonhost.wait_for_open_port(16421) print(pdudaemonhost.succeed("curl 'http://localhost:16421/power/control/on?hostname=testpduhost&port=1'")) with subtest("Connect from client"): clienthost.start() clienthost.wait_until_succeeds("curl 'http://pdudaemonhost:16421/power/control/off?hostname=testpduhost&port=1'") with subtest("Check systemd hardening does not degrade unnoticed"): exact_threshold = 15 service_name = "pdudaemon" pdudaemonhost.fail(f"systemd-analyze security {service_name}.service --threshold={exact_threshold-1}") pdudaemonhost.succeed(f"systemd-analyze security {service_name}.service --threshold={exact_threshold}") ''; }
pkgs/by-name/pd/pdudaemon/package.nix 0 → 100644 +62 −0 Original line number Diff line number Diff line { lib, fetchFromGitHub, python3Packages, nixosTests, }: python3Packages.buildPythonApplication (finalAttrs: { pname = "pdudaemon"; version = "1.1.1"; pyproject = true; src = fetchFromGitHub { owner = "pdudaemon"; repo = "pdudaemon"; tag = finalAttrs.version; hash = "sha256-YjM1RmsdRfNyxCzK+PmSH8n7ZJ3qeIskTPxu2+EaupQ="; }; build-system = with python3Packages; [ setuptools setuptools-scm ]; dependencies = with python3Packages; [ aiohttp requests pexpect systemd-python paramiko pyserial hidapi pysnmp pyasn1 pyusb pymodbus ]; nativeCheckInputs = with python3Packages; [ pytest-asyncio pytest-mock pytestCheckHook ]; __structuredAttrs = true; passthru.tests = { inherit (nixosTests) pdudaemon; }; meta = { changelog = "https://github.com/pdudaemon/pdudaemon/releases/tag/${finalAttrs.src.tag}"; description = "Python Daemon for controlling/sequentially executing commands to PDUs (Power Distribution Units)"; homepage = "https://github.com/pdudaemon/pdudaemon"; license = lib.licenses.gpl2Plus; maintainers = with lib.maintainers; [ aiyion emantor ]; mainProgram = "pdudaemon"; }; })