Unverified Commit a3708ce9 authored by Martin Weinelt's avatar Martin Weinelt Committed by GitHub
Browse files

Merge pull request #230380 from graham33/feature/zwave-js-server_module

zwave-js: module init, zwave-js-server: init at 1.33.0
parents 0d930cd6 b2fccae8
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -104,6 +104,8 @@

- hardware/infiniband.nix adds infiniband subnet manager support using an [opensm](https://github.com/linux-rdma/opensm) systemd-template service, instantiated on card guids. The module also adds kernel modules and cli tooling to help administrators debug and measure performance. Available as [hardware.infiniband.enable](#opt-hardware.infiniband.enable).

- [zwave-js](https://github.com/zwave-js/zwave-js-server), a small server wrapper around Z-Wave JS to access it via a WebSocket. Available as [services.zwave-js](#opt-services.zwave-js.enable).

- [Honk](https://humungus.tedunangst.com/r/honk), a complete ActivityPub server with minimal setup and support costs.
  Available as [services.honk](#opt-services.honk.enable).

+1 −0
Original line number Diff line number Diff line
@@ -564,6 +564,7 @@
  ./services/home-automation/home-assistant.nix
  ./services/home-automation/homeassistant-satellite.nix
  ./services/home-automation/zigbee2mqtt.nix
  ./services/home-automation/zwave-js.nix
  ./services/logging/SystemdJournal2Gelf.nix
  ./services/logging/awstats.nix
  ./services/logging/filebeat.nix
+152 −0
Original line number Diff line number Diff line
{config, pkgs, lib, ...}:

with lib;

let
  cfg = config.services.zwave-js;
  mergedConfigFile = "/run/zwave-js/config.json";
  settingsFormat = pkgs.formats.json {};
in {
  options.services.zwave-js = {
    enable = mkEnableOption (mdDoc "the zwave-js server on boot");

    package = mkPackageOptionMD pkgs "zwave-js-server" { };

    port = mkOption {
      type = types.port;
      default = 3000;
      description = mdDoc ''
        Port for the server to listen on.
      '';
    };

    serialPort = mkOption {
      type = types.path;
      description = mdDoc ''
        Serial port device path for Z-Wave controller.
      '';
      example = "/dev/ttyUSB0";
    };

    secretsConfigFile = mkOption {
      type = types.path;
      description = mdDoc ''
        JSON file containing secret keys. A dummy example:

        ```
        {
          "securityKeys": {
            "S0_Legacy": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
            "S2_Unauthenticated": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
            "S2_Authenticated": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
            "S2_AccessControl": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
          }
        }
        ```

        See
        <https://zwave-js.github.io/node-zwave-js/#/getting-started/security-s2>
        for details. This file will be merged with the module-generated config
        file (taking precedence).

        Z-Wave keys can be generated with:

          {command}`< /dev/urandom tr -dc A-F0-9 | head -c32 ;echo`


        ::: {.warning}
        A file in the nix store should not be used since it will be readable to
        all users.
        :::
      '';
      example = "/secrets/zwave-js-keys.json";
    };

    settings = mkOption {
      type = lib.types.submodule {
        freeformType = settingsFormat.type;

        options = {
          storage = {
            cacheDir = mkOption {
              type = types.path;
              default = "/var/cache/zwave-js";
              readOnly = true;
              description = lib.mdDoc "Cache directory";
            };
          };
        };
      };
      default = {};
      description = mdDoc ''
        Configuration settings for the generated config
        file.
      '';
    };

    extraFlags = lib.mkOption {
      type = with lib.types; listOf str;
      default = [ ];
      example = [ "--mock-driver" ];
      description = lib.mdDoc ''
        Extra flags to pass to command
      '';
    };
  };

  config = mkIf cfg.enable {
    systemd.services.zwave-js = let
      configFile = settingsFormat.generate "zwave-js-config.json" cfg.settings;
    in {
      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ];
      description = "Z-Wave JS Server";
      serviceConfig = {
        ExecStartPre = ''
          /bin/sh -c "${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${configFile} ${cfg.secretsConfigFile} > ${mergedConfigFile}"
        '';
        ExecStart = lib.concatStringsSep " " [
          "${cfg.package}/bin/zwave-server"
          "--config ${mergedConfigFile}"
          "--port ${toString cfg.port}"
          cfg.serialPort
          (escapeShellArgs cfg.extraFlags)
        ];
        Restart = "on-failure";
        User = "zwave-js";
        SupplementaryGroups = [ "dialout" ];
        CacheDirectory = "zwave-js";
        RuntimeDirectory = "zwave-js";

        # Hardening
        CapabilityBoundingSet = "";
        DeviceAllow = [cfg.serialPort];
        DevicePolicy = "closed";
        DynamicUser = true;
        LockPersonality = true;
        MemoryDenyWriteExecute = false;
        NoNewPrivileges = true;
        PrivateUsers = true;
        PrivateTmp = true;
        ProtectClock = true;
        ProtectControlGroups = true;
        ProtectHome = true;
        ProtectHostname = true;
        ProtectKernelLogs = true;
        ProtectKernelModules = true;
        RemoveIPC = true;
        RestrictNamespaces = true;
        RestrictRealtime = true;
        RestrictSUIDSGID = true;
        SystemCallArchitectures = "native";
        SystemCallFilter = [
          "@system-service @pkey"
          "~@privileged @resources"
        ];
        UMask = "0077";
      };
    };
  };

  meta.maintainers = with lib.maintainers; [ graham33 ];
}
+1 −0
Original line number Diff line number Diff line
@@ -934,4 +934,5 @@ in {
  zram-generator = handleTest ./zram-generator.nix {};
  zrepl = handleTest ./zrepl.nix {};
  zsh-history = handleTest ./zsh-history.nix {};
  zwave-js = handleTest ./zwave-js.nix {};
}
+31 −0
Original line number Diff line number Diff line
import ./make-test-python.nix ({ pkgs, lib, ...} :

let
  secretsConfigFile = pkgs.writeText "secrets.json" (builtins.toJSON {
    securityKeys = {
      "S0_Legacy" = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
    };
  });
in {
  name = "zwave-js";
  meta.maintainers = with lib.maintainers; [ graham33 ];

  nodes = {
    machine = { config, ... }: {
      services.zwave-js = {
        enable = true;
        serialPort = "/dev/null";
        extraFlags = ["--mock-driver"];
        inherit secretsConfigFile;
      };
    };
  };

  testScript = ''
    start_all()

    machine.wait_for_unit("zwave-js.service")
    machine.wait_for_open_port(3000)
    machine.wait_until_succeeds("journalctl --since -1m --unit zwave-js --grep 'ZwaveJS server listening'")
  '';
})
Loading