at 25.11-pre 5.8 kB view raw
1{ 2 config, 3 lib, 4 pkgs, 5 ... 6}: 7let 8 cfg = config.services.keyd; 9 10 keyboardOptions = 11 { ... }: 12 { 13 options = { 14 ids = lib.mkOption { 15 type = lib.types.listOf lib.types.str; 16 default = [ "*" ]; 17 example = [ 18 "*" 19 "-0123:0456" 20 ]; 21 description = '' 22 Device identifiers, as shown by {manpage}`keyd(1)`. 23 ''; 24 }; 25 26 settings = lib.mkOption { 27 type = (pkgs.formats.ini { }).type; 28 default = { }; 29 example = { 30 main = { 31 capslock = "overload(control, esc)"; 32 rightalt = "layer(rightalt)"; 33 }; 34 35 rightalt = { 36 j = "down"; 37 k = "up"; 38 h = "left"; 39 l = "right"; 40 }; 41 }; 42 description = '' 43 Configuration, except `ids` section, that is written to {file}`/etc/keyd/<keyboard>.conf`. 44 Appropriate names can be used to write non-alpha keys, for example "equal" instead of "=" sign (see <https://github.com/NixOS/nixpkgs/issues/236622>). 45 See <https://github.com/rvaiya/keyd> how to configure. 46 ''; 47 }; 48 49 extraConfig = lib.mkOption { 50 type = lib.types.lines; 51 default = ""; 52 example = '' 53 [control+shift] 54 h = left 55 ''; 56 description = '' 57 Extra configuration that is appended to the end of the file. 58 **Do not** write `ids` section here, use a separate option for it. 59 You can use this option to define compound layers that must always be defined after the layer they are comprised. 60 ''; 61 }; 62 }; 63 }; 64in 65{ 66 imports = [ 67 (lib.mkRemovedOptionModule [ "services" "keyd" "ids" ] 68 ''Use keyboards.<filename>.ids instead. If you don't need a multi-file configuration, just add keyboards.default before the ids. See https://github.com/NixOS/nixpkgs/pull/243271.'' 69 ) 70 (lib.mkRemovedOptionModule [ "services" "keyd" "settings" ] 71 ''Use keyboards.<filename>.settings instead. If you don't need a multi-file configuration, just add keyboards.default before the settings. See https://github.com/NixOS/nixpkgs/pull/243271.'' 72 ) 73 ]; 74 75 options.services.keyd = { 76 enable = lib.mkEnableOption "keyd, a key remapping daemon"; 77 78 keyboards = lib.mkOption { 79 type = lib.types.attrsOf (lib.types.submodule keyboardOptions); 80 default = { }; 81 example = lib.literalExpression '' 82 { 83 default = { 84 ids = [ "*" ]; 85 settings = { 86 main = { 87 capslock = "overload(control, esc)"; 88 }; 89 }; 90 }; 91 externalKeyboard = { 92 ids = [ "1ea7:0907" ]; 93 settings = { 94 main = { 95 esc = capslock; 96 }; 97 }; 98 }; 99 } 100 ''; 101 description = '' 102 Configuration for one or more device IDs. Corresponding files in the /etc/keyd/ directory are created according to the name of the keys (like `default` or `externalKeyboard`). 103 ''; 104 }; 105 }; 106 107 config = lib.mkIf cfg.enable { 108 # Creates separate files in the `/etc/keyd/` directory for each key in the dictionary 109 environment.etc = lib.mapAttrs' ( 110 name: options: 111 lib.nameValuePair "keyd/${name}.conf" { 112 text = '' 113 [ids] 114 ${lib.concatStringsSep "\n" options.ids} 115 116 ${lib.generators.toINI { } options.settings} 117 ${options.extraConfig} 118 ''; 119 } 120 ) cfg.keyboards; 121 122 hardware.uinput.enable = lib.mkDefault true; 123 124 systemd.services.keyd = { 125 description = "Keyd remapping daemon"; 126 documentation = [ "man:keyd(1)" ]; 127 128 wantedBy = [ "multi-user.target" ]; 129 130 restartTriggers = lib.mapAttrsToList ( 131 name: options: config.environment.etc."keyd/${name}.conf".source 132 ) cfg.keyboards; 133 134 # this is configurable in 2.4.2, later versions seem to remove this option. 135 # post-2.4.2 may need to set makeFlags in the derivation: 136 # 137 # makeFlags = [ "SOCKET_PATH/run/keyd/keyd.socket" ]; 138 environment.KEYD_SOCKET = "/run/keyd/keyd.sock"; 139 140 serviceConfig = { 141 ExecStart = "${pkgs.keyd}/bin/keyd"; 142 Restart = "always"; 143 144 # TODO investigate why it doesn't work propeprly with DynamicUser 145 # See issue: https://github.com/NixOS/nixpkgs/issues/226346 146 # DynamicUser = true; 147 SupplementaryGroups = [ 148 config.users.groups.input.name 149 config.users.groups.uinput.name 150 ]; 151 152 RuntimeDirectory = "keyd"; 153 154 # Hardening 155 CapabilityBoundingSet = [ "CAP_SYS_NICE" ]; 156 DeviceAllow = [ 157 "char-input rw" 158 "/dev/uinput rw" 159 ]; 160 ProtectClock = true; 161 PrivateNetwork = true; 162 ProtectHome = true; 163 ProtectHostname = true; 164 PrivateUsers = false; 165 PrivateMounts = true; 166 PrivateTmp = true; 167 RestrictNamespaces = true; 168 ProtectKernelLogs = true; 169 ProtectKernelModules = true; 170 ProtectKernelTunables = true; 171 ProtectControlGroups = true; 172 MemoryDenyWriteExecute = true; 173 RestrictRealtime = true; 174 LockPersonality = true; 175 ProtectProc = "invisible"; 176 SystemCallFilter = [ 177 "nice" 178 "@system-service" 179 "~@privileged" 180 ]; 181 RestrictAddressFamilies = [ "AF_UNIX" ]; 182 RestrictSUIDSGID = true; 183 IPAddressDeny = [ "any" ]; 184 NoNewPrivileges = true; 185 ProtectSystem = "strict"; 186 ProcSubset = "pid"; 187 UMask = "0077"; 188 }; 189 }; 190 }; 191}