nixos/preload: init
[NixPkgs.git] / nixos / modules / services / home-automation / zwave-js.nix
blob87c9b8f1ac81bb1216f258953afb2082a5e88a17
1 {config, pkgs, lib, ...}:
3 with lib;
5 let
6   cfg = config.services.zwave-js;
7   mergedConfigFile = "/run/zwave-js/config.json";
8   settingsFormat = pkgs.formats.json {};
9 in {
10   options.services.zwave-js = {
11     enable = mkEnableOption (mdDoc "the zwave-js server on boot");
13     package = mkPackageOptionMD pkgs "zwave-js-server" { };
15     port = mkOption {
16       type = types.port;
17       default = 3000;
18       description = mdDoc ''
19         Port for the server to listen on.
20       '';
21     };
23     serialPort = mkOption {
24       type = types.path;
25       description = mdDoc ''
26         Serial port device path for Z-Wave controller.
27       '';
28       example = "/dev/ttyUSB0";
29     };
31     secretsConfigFile = mkOption {
32       type = types.path;
33       description = mdDoc ''
34         JSON file containing secret keys. A dummy example:
36         ```
37         {
38           "securityKeys": {
39             "S0_Legacy": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
40             "S2_Unauthenticated": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
41             "S2_Authenticated": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
42             "S2_AccessControl": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
43           }
44         }
45         ```
47         See
48         <https://zwave-js.github.io/node-zwave-js/#/getting-started/security-s2>
49         for details. This file will be merged with the module-generated config
50         file (taking precedence).
52         Z-Wave keys can be generated with:
54           {command}`< /dev/urandom tr -dc A-F0-9 | head -c32 ;echo`
57         ::: {.warning}
58         A file in the nix store should not be used since it will be readable to
59         all users.
60         :::
61       '';
62       example = "/secrets/zwave-js-keys.json";
63     };
65     settings = mkOption {
66       type = lib.types.submodule {
67         freeformType = settingsFormat.type;
69         options = {
70           storage = {
71             cacheDir = mkOption {
72               type = types.path;
73               default = "/var/cache/zwave-js";
74               readOnly = true;
75               description = lib.mdDoc "Cache directory";
76             };
77           };
78         };
79       };
80       default = {};
81       description = mdDoc ''
82         Configuration settings for the generated config
83         file.
84       '';
85     };
87     extraFlags = lib.mkOption {
88       type = with lib.types; listOf str;
89       default = [ ];
90       example = [ "--mock-driver" ];
91       description = lib.mdDoc ''
92         Extra flags to pass to command
93       '';
94     };
95   };
97   config = mkIf cfg.enable {
98     systemd.services.zwave-js = let
99       configFile = settingsFormat.generate "zwave-js-config.json" cfg.settings;
100     in {
101       wantedBy = [ "multi-user.target" ];
102       after = [ "network.target" ];
103       description = "Z-Wave JS Server";
104       serviceConfig = {
105         ExecStartPre = ''
106           /bin/sh -c "${pkgs.jq}/bin/jq -s '.[0] * .[1]' ${configFile} ${cfg.secretsConfigFile} > ${mergedConfigFile}"
107         '';
108         ExecStart = lib.concatStringsSep " " [
109           "${cfg.package}/bin/zwave-server"
110           "--config ${mergedConfigFile}"
111           "--port ${toString cfg.port}"
112           cfg.serialPort
113           (escapeShellArgs cfg.extraFlags)
114         ];
115         Restart = "on-failure";
116         User = "zwave-js";
117         SupplementaryGroups = [ "dialout" ];
118         CacheDirectory = "zwave-js";
119         RuntimeDirectory = "zwave-js";
121         # Hardening
122         CapabilityBoundingSet = "";
123         DeviceAllow = [cfg.serialPort];
124         DevicePolicy = "closed";
125         DynamicUser = true;
126         LockPersonality = true;
127         MemoryDenyWriteExecute = false;
128         NoNewPrivileges = true;
129         PrivateUsers = true;
130         PrivateTmp = true;
131         ProtectClock = true;
132         ProtectControlGroups = true;
133         ProtectHome = true;
134         ProtectHostname = true;
135         ProtectKernelLogs = true;
136         ProtectKernelModules = true;
137         RemoveIPC = true;
138         RestrictNamespaces = true;
139         RestrictRealtime = true;
140         RestrictSUIDSGID = true;
141         SystemCallArchitectures = "native";
142         SystemCallFilter = [
143           "@system-service @pkey"
144           "~@privileged @resources"
145         ];
146         UMask = "0077";
147       };
148     };
149   };
151   meta.maintainers = with lib.maintainers; [ graham33 ];