grafana-alloy: don't build the frontend twice
[NixPkgs.git] / nixos / modules / services / security / usbguard.nix
blob17bffa57ef3ad02cb8bd3c03c42af5670355e3e4
1 { config, lib, pkgs, ... }:
3 with lib;
4 let
5   cfg = config.services.usbguard;
7   # valid policy options
8   policy = (types.enum [ "allow" "block" "reject" "keep" "apply-policy" ]);
10   # decide what file to use for rules
11   ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else cfg.ruleFile;
13   daemonConf = ''
14     # generated by nixos/modules/services/security/usbguard.nix
15     RuleFile=${ruleFile}
16     ImplicitPolicyTarget=${cfg.implicitPolicyTarget}
17     PresentDevicePolicy=${cfg.presentDevicePolicy}
18     PresentControllerPolicy=${cfg.presentControllerPolicy}
19     InsertedDevicePolicy=${cfg.insertedDevicePolicy}
20     RestoreControllerDeviceState=${boolToString cfg.restoreControllerDeviceState}
21     # this does not seem useful for endusers to change
22     DeviceManagerBackend=uevent
23     IPCAllowedUsers=${concatStringsSep " " cfg.IPCAllowedUsers}
24     IPCAllowedGroups=${concatStringsSep " " cfg.IPCAllowedGroups}
25     IPCAccessControlFiles=/var/lib/usbguard/IPCAccessControl.d/
26     DeviceRulesWithPort=${boolToString cfg.deviceRulesWithPort}
27     # HACK: that way audit logs still land in the journal
28     AuditFilePath=/dev/null
29   '';
31   daemonConfFile = pkgs.writeText "usbguard-daemon-conf" daemonConf;
36   ###### interface
38   options = {
39     services.usbguard = {
40       enable = mkEnableOption "USBGuard daemon";
42       package = mkPackageOption pkgs "usbguard" {
43         extraDescription = ''
44           If you do not need the Qt GUI, use `pkgs.usbguard-nox` to save disk space.
45         '';
46       };
48       ruleFile = mkOption {
49         type = types.nullOr types.path;
50         default = "/var/lib/usbguard/rules.conf";
51         example = "/run/secrets/usbguard-rules";
52         description = ''
53           This tells the USBGuard daemon which file to load as policy rule set.
55           The file can be changed manually or via the IPC interface assuming it has the right file permissions.
57           For more details see {manpage}`usbguard-rules.conf(5)`.
58         '';
60       };
61       rules = mkOption {
62         type = types.nullOr types.lines;
63         default = null;
64         example = ''
65           allow with-interface equals { 08:*:* }
66         '';
67         description = ''
68           The USBGuard daemon will load this as the policy rule set.
69           As these rules are NixOS managed they are immutable and can't
70           be changed by the IPC interface.
72           If you do not set this option, the USBGuard daemon will load
73           it's policy rule set from the option configured in `services.usbguard.ruleFile`.
75           Running `usbguard generate-policy` as root will
76           generate a config for your currently plugged in devices.
78           For more details see {manpage}`usbguard-rules.conf(5)`.
79         '';
80       };
82       implicitPolicyTarget = mkOption {
83         type = types.enum [ "allow" "block" "reject" ];
84         default = "block";
85         description = ''
86           How to treat USB devices that don't match any rule in the policy.
87           Target should be one of allow, block or reject (logically remove the
88           device node from the system).
89         '';
90       };
92       presentDevicePolicy = mkOption {
93         type = policy;
94         default = "apply-policy";
95         description = ''
96           How to treat USB devices that are already connected when the daemon
97           starts. Policy should be one of allow, block, reject, keep (keep
98           whatever state the device is currently in) or apply-policy (evaluate
99           the rule set for every present device).
100         '';
101       };
103       presentControllerPolicy = mkOption {
104         type = policy;
105         default = "keep";
106         description = ''
107           How to treat USB controller devices that are already connected when
108           the daemon starts. One of allow, block, reject, keep or apply-policy.
109         '';
110       };
112       insertedDevicePolicy = mkOption {
113         type = types.enum [ "block" "reject" "apply-policy" ];
114         default = "apply-policy";
115         description = ''
116           How to treat USB devices that are already connected after the daemon
117           starts. One of block, reject, apply-policy.
118         '';
119       };
121       restoreControllerDeviceState = mkOption {
122         type = types.bool;
123         default = false;
124         description = ''
125           The  USBGuard  daemon  modifies  some attributes of controller
126           devices like the default authorization state of new child device
127           instances. Using this setting, you can control whether the daemon
128           will try to restore the attribute values to the state before
129           modification on shutdown.
130         '';
131       };
133       IPCAllowedUsers = mkOption {
134         type = types.listOf types.str;
135         default = [ "root" ];
136         example = [ "root" "yourusername" ];
137         description = ''
138           A list of usernames that the daemon will accept IPC connections from.
139         '';
140       };
142       IPCAllowedGroups = mkOption {
143         type = types.listOf types.str;
144         default = [ ];
145         example = [ "wheel" ];
146         description = ''
147           A list of groupnames that the daemon will accept IPC connections
148           from.
149         '';
150       };
152       deviceRulesWithPort = mkOption {
153         type = types.bool;
154         default = false;
155         description = ''
156           Generate device specific rules including the "via-port" attribute.
157         '';
158       };
160       dbus.enable = mkEnableOption "USBGuard dbus daemon";
161     };
162   };
165   ###### implementation
167   config = mkIf cfg.enable {
169     environment.systemPackages = [ cfg.package ];
171     systemd.services = {
172       usbguard = {
173         description = "USBGuard daemon";
175         wantedBy = [ "basic.target" ];
176         wants = [ "systemd-udevd.service" ];
178         # make sure an empty rule file exists
179         preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}'';
181         serviceConfig = {
182           Type = "simple";
183           ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}";
184           Restart = "on-failure";
186           StateDirectory = [
187             "usbguard"
188             "usbguard/IPCAccessControl.d"
189           ];
191           AmbientCapabilities = "";
192           CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER";
193           DeviceAllow = "/dev/null rw";
194           DevicePolicy = "strict";
195           IPAddressDeny = "any";
196           LockPersonality = true;
197           MemoryDenyWriteExecute = true;
198           NoNewPrivileges = true;
199           PrivateDevices = true;
200           PrivateTmp = true;
201           ProtectControlGroups = true;
202           ProtectHome = true;
203           ProtectKernelModules = true;
204           ProtectSystem = true;
205           ReadOnlyPaths = "-/";
206           ReadWritePaths = "-/dev/shm -/tmp";
207           RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" ];
208           RestrictNamespaces = true;
209           RestrictRealtime = true;
210           SystemCallArchitectures = "native";
211           SystemCallFilter = "@system-service";
212           UMask = "0077";
213         };
214       };
216       usbguard-dbus = mkIf cfg.dbus.enable {
217         description = "USBGuard D-Bus Service";
219         wantedBy = [ "multi-user.target" ];
220         requires = [ "usbguard.service" ];
222         serviceConfig = {
223           Type = "dbus";
224           BusName = "org.usbguard1";
225           ExecStart = "${cfg.package}/bin/usbguard-dbus --system";
226           Restart = "on-failure";
227         };
229         aliases = [ "dbus-org.usbguard.service" ];
230       };
231     };
233     security.polkit.extraConfig =
234       let
235         groupCheck = (lib.concatStrings (map
236           (g: "subject.isInGroup(\"${g}\") || ")
237           cfg.IPCAllowedGroups))
238         + "false";
239       in
240       optionalString cfg.dbus.enable ''
241         polkit.addRule(function(action, subject) {
242             if ((action.id == "org.usbguard.Policy1.listRules" ||
243                  action.id == "org.usbguard.Policy1.appendRule" ||
244                  action.id == "org.usbguard.Policy1.removeRule" ||
245                  action.id == "org.usbguard.Devices1.applyDevicePolicy" ||
246                  action.id == "org.usbguard.Devices1.listDevices" ||
247                  action.id == "org.usbguard1.getParameter" ||
248                  action.id == "org.usbguard1.setParameter") &&
249                 subject.active == true && subject.local == true &&
250                 (${groupCheck})) {
251                     return polkit.Result.YES;
252             }
253         });
254       '';
255   };
256   imports = [
257     (mkRemovedOptionModule [ "services" "usbguard" "IPCAccessControlFiles" ] "The usbguard module now hardcodes IPCAccessControlFiles to /var/lib/usbguard/IPCAccessControl.d.")
258     (mkRemovedOptionModule [ "services" "usbguard" "auditFilePath" ] "Removed usbguard module audit log files. Audit logs can be found in the systemd journal.")
259     (mkRenamedOptionModule [ "services" "usbguard" "implictPolicyTarget" ] [ "services" "usbguard" "implicitPolicyTarget" ])
260   ];