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