Unverified Commit 29f8a25c authored by Stanisław Pitucha's avatar Stanisław Pitucha Committed by GitHub
Browse files

pdudaemon: init at 1.1.1 (#499230)

parents 7914bec1 95080024
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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
+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" ];
    };
  };
}
+1 −0
Original line number Diff line number Diff line
@@ -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 { };
+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}")
    '';
}
+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";
  };
})