nixos/preload: init
[NixPkgs.git] / nixos / modules / services / hardware / keyd.nix
blob724e9b95684780db2f7da1ef03293db0429b4464
1 { config, lib, pkgs, ... }:
2 with lib;
3 let
4   cfg = config.services.keyd;
6   keyboardOptions = { ... }: {
7     options = {
8       ids = mkOption {
9         type = types.listOf types.str;
10         default = [ "*" ];
11         example = [ "*" "-0123:0456" ];
12         description = lib.mdDoc ''
13           Device identifiers, as shown by {manpage}`keyd(1)`.
14         '';
15       };
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           };
26           rightalt = {
27             j = "down";
28             k = "up";
29             h = "left";
30             l = "right";
31           };
32         };
33         description = lib.mdDoc ''
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       };
40       extraConfig = mkOption {
41         type = types.lines;
42         default = "";
43         example = ''
44           [control+shift]
45           h = left
46         '';
47         description = lib.mdDoc ''
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   };
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   ];
64   options.services.keyd = {
65     enable = mkEnableOption (lib.mdDoc "keyd, a key remapping daemon");
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 = mdDoc ''
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   };
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}
105             ${generators.toINI {} options.settings}
106             ${options.extraConfig}
107           '';
108         })
109       cfg.keyboards;
111     hardware.uinput.enable = lib.mkDefault true;
113     systemd.services.keyd = {
114       description = "Keyd remapping daemon";
115       documentation = [ "man:keyd(1)" ];
117       wantedBy = [ "multi-user.target" ];
119       restartTriggers = mapAttrsToList
120         (name: options:
121           config.environment.etc."keyd/${name}.conf".source
122         )
123         cfg.keyboards;
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";
131       serviceConfig = {
132         ExecStart = "${pkgs.keyd}/bin/keyd";
133         Restart = "always";
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         ];
143         RuntimeDirectory = "keyd";
145         # Hardening
146         CapabilityBoundingSet = "";
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 = true;
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           "@system-service"
169           "~@privileged"
170           "~@resources"
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   };