at 24.11-pre 4.2 kB view raw
1{config, pkgs, lib, ...}: 2 3with lib; 4 5let 6 cfg = config.services.zwave-js; 7 mergedConfigFile = "/run/zwave-js/config.json"; 8 settingsFormat = pkgs.formats.json {}; 9in { 10 options.services.zwave-js = { 11 enable = mkEnableOption "the zwave-js server on boot"; 12 13 package = mkPackageOption pkgs "zwave-js-server" { }; 14 15 port = mkOption { 16 type = types.port; 17 default = 3000; 18 description = '' 19 Port for the server to listen on. 20 ''; 21 }; 22 23 serialPort = mkOption { 24 type = types.path; 25 description = '' 26 Serial port device path for Z-Wave controller. 27 ''; 28 example = "/dev/ttyUSB0"; 29 }; 30 31 secretsConfigFile = mkOption { 32 type = types.path; 33 description = '' 34 JSON file containing secret keys. A dummy example: 35 36 ``` 37 { 38 "securityKeys": { 39 "S0_Legacy": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", 40 "S2_Unauthenticated": "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB", 41 "S2_Authenticated": "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC", 42 "S2_AccessControl": "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD" 43 } 44 } 45 ``` 46 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). 51 52 Z-Wave keys can be generated with: 53 54 {command}`< /dev/urandom tr -dc A-F0-9 | head -c32 ;echo` 55 56 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 }; 64 65 settings = mkOption { 66 type = lib.types.submodule { 67 freeformType = settingsFormat.type; 68 69 options = { 70 storage = { 71 cacheDir = mkOption { 72 type = types.path; 73 default = "/var/cache/zwave-js"; 74 readOnly = true; 75 description = "Cache directory"; 76 }; 77 }; 78 }; 79 }; 80 default = {}; 81 description = '' 82 Configuration settings for the generated config 83 file. 84 ''; 85 }; 86 87 extraFlags = lib.mkOption { 88 type = with lib.types; listOf str; 89 default = [ ]; 90 example = [ "--mock-driver" ]; 91 description = '' 92 Extra flags to pass to command 93 ''; 94 }; 95 }; 96 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"; 120 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 }; 150 151 meta.maintainers = with lib.maintainers; [ graham33 ]; 152}