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