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 defaultRuleFile = "/var/lib/usbguard/rules.conf";
11
12 # decide what file to use for rules
13 ruleFile = if cfg.rules != null then pkgs.writeText "usbguard-rules" cfg.rules else defaultRuleFile;
14
15 daemonConf = ''
16 # generated by nixos/modules/services/security/usbguard.nix
17 RuleFile=${ruleFile}
18 ImplicitPolicyTarget=${cfg.implictPolicyTarget}
19 PresentDevicePolicy=${cfg.presentDevicePolicy}
20 PresentControllerPolicy=${cfg.presentControllerPolicy}
21 InsertedDevicePolicy=${cfg.insertedDevicePolicy}
22 RestoreControllerDeviceState=${boolToString cfg.restoreControllerDeviceState}
23 # this does not seem useful for endusers to change
24 DeviceManagerBackend=uevent
25 IPCAllowedUsers=${concatStringsSep " " cfg.IPCAllowedUsers}
26 IPCAllowedGroups=${concatStringsSep " " cfg.IPCAllowedGroups}
27 IPCAccessControlFiles=/var/lib/usbguard/IPCAccessControl.d/
28 DeviceRulesWithPort=${boolToString cfg.deviceRulesWithPort}
29 # HACK: that way audit logs still land in the journal
30 AuditFilePath=/dev/null
31 '';
32
33 daemonConfFile = pkgs.writeText "usbguard-daemon-conf" daemonConf;
34
35in
36{
37
38 ###### interface
39
40 options = {
41 services.usbguard = {
42 enable = mkEnableOption (lib.mdDoc "USBGuard daemon");
43
44 package = mkOption {
45 type = types.package;
46 default = pkgs.usbguard;
47 defaultText = literalExpression "pkgs.usbguard";
48 description = lib.mdDoc ''
49 The usbguard package to use. If you do not need the Qt GUI, use
50 `pkgs.usbguard-nox` to save disk space.
51 '';
52 };
53
54 rules = mkOption {
55 type = types.nullOr types.lines;
56 default = null;
57 example = ''
58 allow with-interface equals { 08:*:* }
59 '';
60 description = lib.mdDoc ''
61 The USBGuard daemon will load this as the policy rule set.
62 As these rules are NixOS managed they are immutable and can't
63 be changed by the IPC interface.
64
65 If you do not set this option, the USBGuard daemon will load
66 it's policy rule set from `${defaultRuleFile}`.
67 This file can be changed manually or via the IPC interface.
68
69 Running `usbguard generate-policy` as root will
70 generate a config for your currently plugged in devices.
71
72 For more details see {manpage}`usbguard-rules.conf(5)`.
73 '';
74 };
75
76 implictPolicyTarget = mkOption {
77 type = policy;
78 default = "block";
79 description = lib.mdDoc ''
80 How to treat USB devices that don't match any rule in the policy.
81 Target should be one of allow, block or reject (logically remove the
82 device node from the system).
83 '';
84 };
85
86 presentDevicePolicy = mkOption {
87 type = policy;
88 default = "apply-policy";
89 description = lib.mdDoc ''
90 How to treat USB devices that are already connected when the daemon
91 starts. Policy should be one of allow, block, reject, keep (keep
92 whatever state the device is currently in) or apply-policy (evaluate
93 the rule set for every present device).
94 '';
95 };
96
97 presentControllerPolicy = mkOption {
98 type = policy;
99 default = "keep";
100 description = lib.mdDoc ''
101 How to treat USB controller devices that are already connected when
102 the daemon starts. One of allow, block, reject, keep or apply-policy.
103 '';
104 };
105
106 insertedDevicePolicy = mkOption {
107 type = policy;
108 default = "apply-policy";
109 description = lib.mdDoc ''
110 How to treat USB devices that are already connected after the daemon
111 starts. One of block, reject, apply-policy.
112 '';
113 };
114
115 restoreControllerDeviceState = mkOption {
116 type = types.bool;
117 default = false;
118 description = lib.mdDoc ''
119 The USBGuard daemon modifies some attributes of controller
120 devices like the default authorization state of new child device
121 instances. Using this setting, you can control whether the daemon
122 will try to restore the attribute values to the state before
123 modification on shutdown.
124 '';
125 };
126
127 IPCAllowedUsers = mkOption {
128 type = types.listOf types.str;
129 default = [ "root" ];
130 example = [ "root" "yourusername" ];
131 description = lib.mdDoc ''
132 A list of usernames that the daemon will accept IPC connections from.
133 '';
134 };
135
136 IPCAllowedGroups = mkOption {
137 type = types.listOf types.str;
138 default = [ ];
139 example = [ "wheel" ];
140 description = lib.mdDoc ''
141 A list of groupnames that the daemon will accept IPC connections
142 from.
143 '';
144 };
145
146 deviceRulesWithPort = mkOption {
147 type = types.bool;
148 default = false;
149 description = lib.mdDoc ''
150 Generate device specific rules including the "via-port" attribute.
151 '';
152 };
153 };
154 };
155
156
157 ###### implementation
158
159 config = mkIf cfg.enable {
160
161 environment.systemPackages = [ cfg.package ];
162
163 systemd.services.usbguard = {
164 description = "USBGuard daemon";
165
166 wantedBy = [ "basic.target" ];
167 wants = [ "systemd-udevd.service" ];
168
169 # make sure an empty rule file exists
170 preStart = ''[ -f "${ruleFile}" ] || touch ${ruleFile}'';
171
172 serviceConfig = {
173 Type = "simple";
174 ExecStart = "${cfg.package}/bin/usbguard-daemon -P -k -c ${daemonConfFile}";
175 Restart = "on-failure";
176
177 StateDirectory = [
178 "usbguard"
179 "usbguard/IPCAccessControl.d"
180 ];
181
182 AmbientCapabilities = "";
183 CapabilityBoundingSet = "CAP_CHOWN CAP_FOWNER";
184 DeviceAllow = "/dev/null rw";
185 DevicePolicy = "strict";
186 IPAddressDeny = "any";
187 LockPersonality = true;
188 MemoryDenyWriteExecute = true;
189 NoNewPrivileges = true;
190 PrivateDevices = true;
191 PrivateTmp = true;
192 ProtectControlGroups = true;
193 ProtectHome = true;
194 ProtectKernelModules = true;
195 ProtectSystem = true;
196 ReadOnlyPaths = "-/";
197 ReadWritePaths = "-/dev/shm -/tmp";
198 RestrictAddressFamilies = [ "AF_UNIX" "AF_NETLINK" ];
199 RestrictNamespaces = true;
200 RestrictRealtime = true;
201 SystemCallArchitectures = "native";
202 SystemCallFilter = "@system-service";
203 UMask = "0077";
204 };
205 };
206 };
207 imports = [
208 (mkRemovedOptionModule [ "services" "usbguard" "ruleFile" ] "The usbguard module now uses ${defaultRuleFile} as ruleFile. Alternatively, use services.usbguard.rules to configure rules.")
209 (mkRemovedOptionModule [ "services" "usbguard" "IPCAccessControlFiles" ] "The usbguard module now hardcodes IPCAccessControlFiles to /var/lib/usbguard/IPCAccessControl.d.")
210 (mkRemovedOptionModule [ "services" "usbguard" "auditFilePath" ] "Removed usbguard module audit log files. Audit logs can be found in the systemd journal.")
211 ];
212}