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